UX: Merge notifications and user dropdown
This commit is contained in:
parent
d4b987ff32
commit
3ae5a0a2df
|
@ -1,8 +1,8 @@
|
|||
export default Ember.Component.extend({
|
||||
classNameBindings: ['containerClass'],
|
||||
classNameBindings: ['containerClass', 'condition:visible'],
|
||||
|
||||
containerClass: function() {
|
||||
return (this.get('size') === 'small') ? 'inline-spinner' : undefined;
|
||||
return this.get('size') === 'small' ? 'inline-spinner' : undefined;
|
||||
}.property('size'),
|
||||
|
||||
render: function(buffer) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
export default Ember.Component.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: [':header-dropdown-toggle', 'active'],
|
||||
|
||||
active: Ember.computed.alias('toggleVisible'),
|
||||
|
||||
actions: {
|
||||
toggle() {
|
||||
if (this.siteSettings.login_required && !this.currentUser) {
|
||||
this.sendAction('loginAction');
|
||||
} else {
|
||||
this.toggleProperty('toggleVisible');
|
||||
}
|
||||
this.appEvents.trigger('dropdowns:closeAll');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -55,21 +55,14 @@ export default Ember.Component.extend({
|
|||
@observes('viewMode', 'visible')
|
||||
_visibleChanged() {
|
||||
const isDropdown = (this.get('viewMode') === 'drop-down');
|
||||
const markActive = this.get('markActive');
|
||||
|
||||
if (this.get('visible')) {
|
||||
this.appEvents.on('dropdowns:closeAll', this, this.hide);
|
||||
|
||||
// Allow us to hook into things being shown
|
||||
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onVisible'));
|
||||
|
||||
if (isDropdown && markActive) {
|
||||
$(markActive).addClass('active');
|
||||
}
|
||||
|
||||
$('html').on('click.close-menu-panel', (e) => {
|
||||
const $target = $(e.target);
|
||||
if ($target.closest(markActive).length > 0) { return; }
|
||||
if ($target.closest('.header-dropdown-toggle').length > 0) { return; }
|
||||
if ($target.closest('.menu-panel').length > 0) { return; }
|
||||
this.hide();
|
||||
});
|
||||
|
@ -77,9 +70,6 @@ export default Ember.Component.extend({
|
|||
this._watchSizeChanges();
|
||||
} else {
|
||||
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onHidden'));
|
||||
if (markActive) {
|
||||
$(markActive).removeClass('active');
|
||||
}
|
||||
$('html').off('click.close-menu-panel');
|
||||
this._stopWatchingSize();
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ export default Ember.Component.extend({
|
|||
keyDown(e) {
|
||||
const term = this.get('searchService.term');
|
||||
if (e.which === 13 && term && term.length >= this.siteSettings.min_search_term_length) {
|
||||
this.set('searchVisible', false);
|
||||
this.set('visible', false);
|
||||
this.send('fullSearch');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['user-menu'],
|
||||
notifications: null,
|
||||
loadingNotifications: false,
|
||||
myNotificationsUrl: url('/my/notifications'),
|
||||
|
||||
@observes('visible')
|
||||
_loadNotifications(visible) {
|
||||
if (visible) {
|
||||
this.refreshNotifications();
|
||||
}
|
||||
},
|
||||
|
||||
@observes('currentUser.lastNotificationChange')
|
||||
_resetCachedNotifications() {
|
||||
const visible = this.get('visible');
|
||||
|
||||
if (!Discourse.get("hasFocus")) {
|
||||
this.set('visible', false);
|
||||
this.set('notifications', null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
this.refreshNotifications();
|
||||
} else {
|
||||
this.set('notifications', null);
|
||||
}
|
||||
},
|
||||
|
||||
refreshNotifications() {
|
||||
if (this.get('loadingNotifications')) { return; }
|
||||
this.set("loadingNotifications", true);
|
||||
|
||||
// TODO: It's a bit odd to use the store in a component, but this one really
|
||||
// wants to reach out and grab notifications
|
||||
const store = this.container.lookup('store:main');
|
||||
store.find('notification', {recent: true}).then((notifications) => {
|
||||
this.setProperties({ 'currentUser.unread_notifications': 0, notifications });
|
||||
}).catch(() => {
|
||||
this.set('notifications', null);
|
||||
}).finally(() => {
|
||||
this.set("loadingNotifications", false);
|
||||
});
|
||||
},
|
||||
|
||||
@computed()
|
||||
allowAnon() {
|
||||
return this.siteSettings.allow_anonymous_posting &&
|
||||
(this.get("currentUser.trust_level") >= this.siteSettings.anonymous_posting_min_trust_level ||
|
||||
this.get("isAnon"));
|
||||
},
|
||||
|
||||
isAnon: Ember.computed.alias('currentUser.is_anonymous'),
|
||||
|
||||
actions: {
|
||||
toggleAnon() {
|
||||
Discourse.ajax("/users/toggle-anon", {method: 'POST'}).then(function(){
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,13 +1,11 @@
|
|||
const HeaderController = Ember.Controller.extend({
|
||||
topic: null,
|
||||
showExtraInfo: null,
|
||||
notifications: null,
|
||||
loadingNotifications: false,
|
||||
hamburgerVisible: false,
|
||||
searchVisible: false,
|
||||
userMenuVisible: false,
|
||||
needs: ['application'],
|
||||
|
||||
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
|
||||
canSignUp: Em.computed.alias('controllers.application.canSignUp'),
|
||||
|
||||
showSignUpButton: function() {
|
||||
|
@ -18,70 +16,13 @@ const HeaderController = Ember.Controller.extend({
|
|||
return Discourse.User.current() && !this.get('topic.isPrivateMessage');
|
||||
}.property('topic.isPrivateMessage'),
|
||||
|
||||
_resetCachedNotifications: function() {
|
||||
// a bit hacky, but if we have no focus, hide notifications first
|
||||
const visible = $("#notifications-dropdown").is(":visible");
|
||||
|
||||
if(!Discourse.get("hasFocus")) {
|
||||
if(visible){
|
||||
$("html").click();
|
||||
}
|
||||
this.set("notifications", null);
|
||||
return;
|
||||
}
|
||||
if(visible){
|
||||
this.refreshNotifications();
|
||||
} else {
|
||||
this.set("notifications", null);
|
||||
}
|
||||
}.observes("currentUser.lastNotificationChange"),
|
||||
|
||||
refreshNotifications: function(){
|
||||
const self = this;
|
||||
if (self.get("loadingNotifications")) { return; }
|
||||
|
||||
self.set("loadingNotifications", true);
|
||||
|
||||
this.store.find('notification', {recent: true}).then(function(notifications) {
|
||||
self.setProperties({
|
||||
'currentUser.unread_notifications': 0,
|
||||
notifications
|
||||
});
|
||||
}).catch(function() {
|
||||
self.setProperties({
|
||||
notifications: null
|
||||
});
|
||||
}).finally(function() {
|
||||
self.set("loadingNotifications", false);
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleStar() {
|
||||
const topic = this.get('topic');
|
||||
if (topic) topic.toggleStar();
|
||||
return false;
|
||||
},
|
||||
|
||||
showNotifications(headerView) {
|
||||
const self = this;
|
||||
|
||||
if (self.get('currentUser.unread_notifications') || self.get('currentUser.unread_private_messages') || !self.get('notifications')) {
|
||||
self.refreshNotifications();
|
||||
}
|
||||
headerView.showDropdownBySelector("#user-notifications");
|
||||
},
|
||||
|
||||
toggleSearchMenu() {
|
||||
this.appEvents.trigger('dropdowns:closeAll');
|
||||
this.toggleProperty('searchVisible');
|
||||
},
|
||||
|
||||
toggleHamburgerMenu() {
|
||||
this.appEvents.trigger('dropdowns:closeAll');
|
||||
this.toggleProperty('hamburgerVisible');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
needs: ['header'],
|
||||
loadingNotifications: Em.computed.alias('controllers.header.loadingNotifications'),
|
||||
myNotificationsUrl: url('/my/notifications')
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
export default Ember.ArrayController.extend({
|
||||
showAdminLinks: Em.computed.alias("currentUser.staff"),
|
||||
|
||||
allowAnon: function(){
|
||||
return this.siteSettings.allow_anonymous_posting &&
|
||||
(this.get("currentUser.trust_level") >= this.siteSettings.anonymous_posting_min_trust_level ||
|
||||
this.get("isAnon"));
|
||||
}.property(),
|
||||
|
||||
isAnon: function(){
|
||||
return this.get("currentUser.is_anonymous");
|
||||
}.property(),
|
||||
|
||||
actions: {
|
||||
logout() {
|
||||
Discourse.logout();
|
||||
return false;
|
||||
},
|
||||
toggleAnon() {
|
||||
Discourse.ajax("/users/toggle-anon", {method: 'POST'}).then(function(){
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -77,7 +77,6 @@ export function cleanDOM() {
|
|||
$('.profiler-results .profiler-result').remove();
|
||||
|
||||
// Close some elements that may be open
|
||||
$('.d-dropdown').hide();
|
||||
$('header ul.icons li').removeClass('active');
|
||||
$('[data-toggle="dropdown"]').parent().removeClass('open');
|
||||
// close the lightbox
|
||||
|
|
|
@ -203,13 +203,6 @@ const TopicRoute = Discourse.Route.extend({
|
|||
// In case we navigate from one topic directly to another
|
||||
isTransitioning = false;
|
||||
|
||||
if (Discourse.Mobile.mobileView) {
|
||||
// close the dropdowns on mobile
|
||||
$('.d-dropdown').hide();
|
||||
$('header ul.icons li').removeClass('active');
|
||||
$('[data-toggle="dropdown"]').parent().removeClass('open');
|
||||
}
|
||||
|
||||
controller.setProperties({
|
||||
model,
|
||||
editingTopic: false,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{#menu-panel visible=hamburgerVisible markActive=".hamburger-dropdown"}}
|
||||
<ul class="location-links">
|
||||
{{#menu-panel visible=visible}}
|
||||
<ul class="menu-links">
|
||||
{{#if currentUser.staff}}
|
||||
<li>
|
||||
{{#link-to "admin" class="admin-link"}}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<a {{action "toggle"}} class='icon' href title={{i18n title}} aria-label={{i18n title}} id={{iconId}}>
|
||||
{{#if showUser}}
|
||||
{{bound-avatar currentUser "medium"}}
|
||||
{{else}}
|
||||
{{fa-icon icon}}
|
||||
{{/if}}
|
||||
</a>
|
||||
|
||||
{{yield}}
|
|
@ -1,8 +1,4 @@
|
|||
{{#menu-panel visible=searchVisible
|
||||
markActive=".search-dropdown"
|
||||
onVisible="showedSearch"
|
||||
onHidden="cancelHighlight"}}
|
||||
|
||||
{{#menu-panel visible=visible onVisible="showedSearch" onHidden="cancelHighlight"}}
|
||||
{{plugin-outlet "above-search"}}
|
||||
{{search-text-field value=searchService.term id="search-term"}}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<section class='d-dropdown' id='user-dropdown'>
|
||||
<ul class='user-dropdown-links'>
|
||||
{{#menu-panel visible=visible}}
|
||||
<ul class='menu-links'>
|
||||
<li>{{#link-to 'user' currentUser class="user-activity-link" }}{{i18n 'user.profile'}}{{/link-to}}</li>
|
||||
<li>
|
||||
{{#link-to 'userPrivateMessages.index' currentUser class='user-messages-link'}}
|
||||
|
@ -11,6 +11,21 @@
|
|||
{{#if allowAnon}}
|
||||
<li><a href {{action 'toggleAnon'}}>{{#if isAnon}}{{i18n 'switch_from_anon'}}{{else}}{{i18n 'switch_to_anon'}}{{/if}}</a></li>
|
||||
{{/if}}
|
||||
<li>{{d-button action="logout" class="btn-danger right logout" icon="sign-out" label="user.log_out"}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class='notifications'>
|
||||
{{#conditional-loading-spinner condition=loadingNotifications containerClass="spinner-container"}}
|
||||
<h3>{{i18n "user.notifications"}}</h3>
|
||||
{{#if notifications}}
|
||||
<ul>
|
||||
{{#each notifications as |n|}}
|
||||
{{notification-item notification=n}}
|
||||
{{/each}}
|
||||
<li class="read last">
|
||||
<a href={{myNotificationsUrl}}>{{i18n 'notifications.more'}}…</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{/conditional-loading-spinner}}
|
||||
</div>
|
||||
{{/menu-panel}}
|
|
@ -1,5 +1,6 @@
|
|||
{{hamburger-menu hamburgerVisible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
|
||||
{{search-menu searchVisible=searchVisible}}
|
||||
{{user-menu visible=userMenuVisible}}
|
||||
{{hamburger-menu visible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
|
||||
{{search-menu visible=searchVisible}}
|
||||
|
||||
<div class='wrap'>
|
||||
<div class='contents clearfix'>
|
||||
|
@ -14,75 +15,42 @@
|
|||
{{/unless}}
|
||||
<ul class='icons clearfix' role='navigation'>
|
||||
{{#if currentUser}}
|
||||
|
||||
{{plugin-outlet "header-before-notifications"}}
|
||||
{{/if}}
|
||||
|
||||
<li class='notifications'>
|
||||
<a class='icon' href {{action "showNotifications" target="view"}} data-notifications="notifications-dropdown" id='user-notifications' title='{{i18n 'notifications.title'}}'>
|
||||
{{fa-icon "comment" label="notifications.title"}}
|
||||
</a>
|
||||
{{#header-dropdown iconId="search-button"
|
||||
icon="search"
|
||||
toggleVisible=searchVisible
|
||||
loginAction="showLogin"
|
||||
title="search.title"}}
|
||||
{{/header-dropdown}}
|
||||
|
||||
{{#header-dropdown iconId="toggle-hamburger-menu"
|
||||
icon="bars"
|
||||
toggleVisible=hamburgerVisible
|
||||
loginAction="showLogin"
|
||||
title="hamburger_menu"}}
|
||||
{{#if flaggedPostsCount}}
|
||||
<a href='/admin/flags/active' title='{{i18n 'notifications.total_flagged'}}' class='badge-notification flagged-posts'>{{flaggedPostsCount}}</a>
|
||||
{{/if}}
|
||||
{{/header-dropdown}}
|
||||
|
||||
{{#if currentUser}}
|
||||
{{#header-dropdown iconId="current-user"
|
||||
showUser="true"
|
||||
toggleVisible=userMenuVisible
|
||||
loginAction="showLogin"
|
||||
title="user.avatar.header_title"}}
|
||||
{{#if currentUser.unread_notifications}}
|
||||
<a href class='badge-notification unread-notifications'>{{currentUser.unread_notifications}}</a>
|
||||
{{/if}}
|
||||
{{#if currentUser.unread_private_messages}}
|
||||
<a href class='badge-notification unread-private-messages'>{{currentUser.unread_private_messages}}</a>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/header-dropdown}}
|
||||
{{/if}}
|
||||
<li class="search-dropdown">
|
||||
{{#if loginRequired}}
|
||||
<a id='search-button' class='icon expand' href aria-hidden="true" {{action "showLogin"}}>
|
||||
{{fa-icon "search"}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a {{action "toggleSearchMenu"}} class='icon' href
|
||||
id='search-button'
|
||||
title={{i18n 'search.title'}}>
|
||||
{{fa-icon "search" label="search.title"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</li>
|
||||
<li class='hamburger-dropdown'>
|
||||
{{#if loginRequired}}
|
||||
<a class='icon'
|
||||
href
|
||||
aria-hidden="true"
|
||||
id="toggle-hamburger-menu"
|
||||
{{action "showLogin"}}>
|
||||
{{fa-icon "bars"}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a {{action "toggleHamburgerMenu"}} class='icon' href
|
||||
title={{i18n 'hamburger_menu'}}
|
||||
aria-label={{i18n 'hamburger_menu'}}
|
||||
id="toggle-hamburger-menu">
|
||||
{{fa-icon "bars"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if flaggedPostsCount}}
|
||||
<a href='/admin/flags/active' title='{{i18n 'notifications.total_flagged'}}' class='badge-notification flagged-posts'>{{flaggedPostsCount}}</a>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{#if currentUser}}
|
||||
<li class='current-user dropdown'>
|
||||
<a class='icon'
|
||||
data-dropdown="user-dropdown"
|
||||
data-render="renderUserDropdown"
|
||||
href="#"
|
||||
title='{{i18n 'user.avatar.header_title'}}'
|
||||
aria-label='{{i18n 'user.avatar.header_title'}}'
|
||||
id="current-user">
|
||||
{{bound-avatar currentUser "medium"}}
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
||||
{{#if view.renderDropdowns}}
|
||||
{{plugin-outlet "header-before-dropdowns"}}
|
||||
{{render "notifications" notifications}}
|
||||
{{render "user-dropdown"}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{#if showExtraInfo}}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<section class="d-dropdown" id="notifications-dropdown">
|
||||
{{#conditional-loading-spinner condition=loadingNotifications}}
|
||||
{{#if content}}
|
||||
<ul>
|
||||
{{#each n in model}}
|
||||
{{notification-item notification=n}}
|
||||
{{/each}}
|
||||
<li class="read last">
|
||||
<a href="{{unbound myNotificationsUrl}}">{{i18n 'notifications.more'}}…</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{else}}
|
||||
<div class="none">{{i18n 'notifications.none'}}</div>
|
||||
{{/if}}
|
||||
{{/conditional-loading-spinner}}
|
||||
</section>
|
|
@ -1,105 +1,10 @@
|
|||
let originalZIndex;
|
||||
|
||||
export default Ember.View.extend({
|
||||
tagName: 'header',
|
||||
classNames: ['d-header', 'clearfix'],
|
||||
classNameBindings: ['editingTopic'],
|
||||
templateName: 'header',
|
||||
renderDropdowns: false,
|
||||
|
||||
showDropdown($target) {
|
||||
var self = this;
|
||||
|
||||
this.appEvents.trigger('dropdowns:closeAll');
|
||||
|
||||
if (!this.get("renderDropdowns")) {
|
||||
this.set("renderDropdowns", true);
|
||||
Em.run.next(function(){
|
||||
self.showDropdown($target);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const elementId = $target.data('dropdown') || $target.data('notifications'),
|
||||
$dropdown = $("#" + elementId),
|
||||
$li = $target.closest('li'),
|
||||
$ul = $target.closest('ul'),
|
||||
$html = $('html'),
|
||||
$header = $('header'),
|
||||
replyZIndex = parseInt($('#reply-control').css('z-index'), 10);
|
||||
|
||||
originalZIndex = originalZIndex || $('header').css('z-index');
|
||||
|
||||
if (replyZIndex > 0) {
|
||||
$header.css("z-index", replyZIndex + 1);
|
||||
}
|
||||
|
||||
const controller = self.get('controller');
|
||||
if (controller && !controller.isDestroyed) {
|
||||
controller.set('visibleDropdown', elementId);
|
||||
}
|
||||
// we need to ensure we are rendered,
|
||||
// this optimises the speed of the initial render
|
||||
const render = $target.data('render');
|
||||
if (render){
|
||||
if (!this.get(render)){
|
||||
this.set(render, true);
|
||||
Em.run.next(this, function(){
|
||||
this.showDropdown.apply(self, [$target]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const hideDropdown = function() {
|
||||
$header.css("z-index", originalZIndex);
|
||||
$dropdown.fadeOut('fast');
|
||||
$li.removeClass('active');
|
||||
$html.data('hide-dropdown', null);
|
||||
|
||||
if (controller && !controller.isDestroyed){
|
||||
controller.set('visibleDropdown', null);
|
||||
}
|
||||
$html.off('click.d-dropdown');
|
||||
$dropdown.off('click.d-dropdown');
|
||||
};
|
||||
|
||||
// if a dropdown is active and the user clicks on it, close it
|
||||
if ($li.hasClass('active')) { return hideDropdown(); }
|
||||
// otherwhise, mark it as active
|
||||
$li.addClass('active');
|
||||
// hide the other dropdowns
|
||||
$('li', $ul).not($li).removeClass('active');
|
||||
$('.d-dropdown').not($dropdown).fadeOut('fast');
|
||||
// fade it fast
|
||||
$dropdown.fadeIn('fast');
|
||||
// autofocus any text input field
|
||||
$dropdown.find('input[type=text]').focus().select();
|
||||
|
||||
$html.on('click.d-dropdown', function(e) {
|
||||
return $(e.target).closest('.d-dropdown').length > 0 ? true : hideDropdown();
|
||||
});
|
||||
|
||||
$dropdown.on('click.d-dropdown', function(e) {
|
||||
if(e.shiftKey || e.metaKey || e.ctrlKey || e.which === 2) return true;
|
||||
return $(e.target).closest('a').not('.search-link, .filter-type').length > 0 ? hideDropdown() : true;
|
||||
});
|
||||
|
||||
$html.data('hide-dropdown', hideDropdown);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
showDropdownBySelector: function(selector) {
|
||||
this.showDropdown($(selector));
|
||||
},
|
||||
|
||||
showNotifications: function() {
|
||||
this.get("controller").send("showNotifications", this);
|
||||
},
|
||||
|
||||
examineDockHeader: function() {
|
||||
|
||||
var headerView = this;
|
||||
|
||||
// Check the dock after the current run loop. While rendering,
|
||||
|
@ -124,29 +29,18 @@ export default Ember.View.extend({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_tearDown: function() {
|
||||
$(window).unbind('scroll.discourse-dock');
|
||||
$(document).unbind('touchmove.discourse-dock');
|
||||
this.$('a.unread-private-messages, a.unread-notifications, a[data-notifications]').off('click.notifications');
|
||||
this.$('a[data-dropdown]').off('click.dropdown');
|
||||
$('body').off('keydown.header');
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
_setup: function() {
|
||||
|
||||
const self = this;
|
||||
|
||||
this.$('a[data-dropdown]').on('click.dropdown', function(e) {
|
||||
self.showDropdown.call(self, $(e.currentTarget));
|
||||
return false;
|
||||
});
|
||||
this.$().on('click.notifications','a.unread-private-messages, a.unread-notifications, a[data-notifications]', function(e) {
|
||||
self.showNotifications(e);
|
||||
return false;
|
||||
});
|
||||
$(window).bind('scroll.discourse-dock', function() {
|
||||
self.examineDockHeader();
|
||||
});
|
||||
|
@ -154,22 +48,5 @@ export default Ember.View.extend({
|
|||
self.examineDockHeader();
|
||||
});
|
||||
self.examineDockHeader();
|
||||
|
||||
// Delegate ESC to the composer
|
||||
$('body').on('keydown.header', function(e) {
|
||||
// Hide dropdowns
|
||||
if (e.which === 27) {
|
||||
self.$('li').removeClass('active');
|
||||
self.$('.d-dropdown').fadeOut('fast');
|
||||
}
|
||||
if (self.get('editingTopic')) {
|
||||
if (e.which === 13) {
|
||||
self.finishedEdit();
|
||||
}
|
||||
if (e.which === 27) {
|
||||
return self.cancelEdit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}.on('didInsertElement')
|
||||
});
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
background-color: scale-color($tertiary, $lightness: 50%);
|
||||
}
|
||||
.unread-private-messages {
|
||||
left: -4px;
|
||||
left: 82px;
|
||||
}
|
||||
}
|
||||
.flagged-posts {
|
||||
|
@ -134,96 +134,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.d-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background: $secondary;
|
||||
max-height: 417px;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1100;
|
||||
overflow: auto;
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
padding: 5px;
|
||||
|
||||
box-shadow: 0 2px 2px rgba(0,0,0, .4);
|
||||
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.heading a:hover {
|
||||
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||
}
|
||||
|
||||
|
||||
// Notifications
|
||||
&#notifications-dropdown {
|
||||
.fa { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); }
|
||||
.icon { color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); }
|
||||
li {
|
||||
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||
i {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
span { color: $primary; }
|
||||
&:hover a { background-color: dark-light-diff($highlight, $secondary, 50%, -55%); }
|
||||
a { padding: 4px 0 3px 2px; }
|
||||
p {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.is-warning {
|
||||
i.fa-envelope-o {
|
||||
&:before {
|
||||
content: "\f0e0";
|
||||
}
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
.read {
|
||||
background-color: $secondary;
|
||||
}
|
||||
.none {
|
||||
padding: 5px;
|
||||
}
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-width: 2px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* as a big ol' click target, don't let text inside be selected */
|
||||
@include unselectable;
|
||||
}
|
||||
|
||||
// Search
|
||||
|
||||
|
||||
// Categories
|
||||
|
||||
&#user-dropdown {
|
||||
width: 118px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 2px 8px;
|
||||
margin-bottom: 2px;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.highlight-strong {
|
||||
background-color: dark-light-diff($highlight, $secondary, 40%, -45%);
|
||||
|
|
|
@ -52,8 +52,8 @@
|
|||
|
||||
}
|
||||
|
||||
.hamburger-panel {
|
||||
ul.location-links li, li.heading {
|
||||
.menu-panel {
|
||||
ul.menu-links li, ul li.heading {
|
||||
a {
|
||||
padding: 0.5em;
|
||||
display: block;
|
||||
|
@ -74,6 +74,7 @@
|
|||
background-color: transparent;
|
||||
vertical-align: top;
|
||||
padding: 5px 5px 2px 5px;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,3 +172,58 @@
|
|||
button {margin-left: 5px;}
|
||||
}
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
|
||||
.notifications {
|
||||
h3 {
|
||||
padding: 0 0.4em;
|
||||
font-weight: bold;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.fa { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); }
|
||||
.icon { color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); }
|
||||
li {
|
||||
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||
padding: 0.5em;
|
||||
i {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
span { color: $primary; }
|
||||
&:hover { background-color: dark-light-diff($highlight, $secondary, 50%, -55%); }
|
||||
a { padding: 0; }
|
||||
p {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.is-warning {
|
||||
i.fa-envelope-o {
|
||||
&:before {
|
||||
content: "\f0e0";
|
||||
}
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
.read {
|
||||
background-color: $secondary;
|
||||
}
|
||||
.none {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.spinner-container.visible {
|
||||
min-height: 30px;
|
||||
}
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-width: 2px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* as a big ol' click target, don't let text inside be selected */
|
||||
@include unselectable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.d-dropdown & {
|
||||
.menu-panel & {
|
||||
max-width: 90px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,29 +26,6 @@ and (max-width : 570px) {
|
|||
padding-top: 82px;
|
||||
}
|
||||
|
||||
// Dropdowns
|
||||
// --------------------------------------------------
|
||||
|
||||
.d-dropdown {
|
||||
width: 320px;
|
||||
overflow: auto;
|
||||
// Common
|
||||
|
||||
.heading {
|
||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
}
|
||||
|
||||
// Categories
|
||||
|
||||
.btn {
|
||||
padding: 2px 8px;
|
||||
margin-bottom: 2px;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-link .blurb {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 45%), scale-color($secondary, $lightness: 55%));
|
||||
display: block;
|
||||
|
@ -59,16 +36,3 @@ and (max-width : 570px) {
|
|||
color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%));
|
||||
}
|
||||
}
|
||||
|
||||
.d-dropdown#search-dropdown {
|
||||
width: 540px;
|
||||
}
|
||||
|
||||
.d-dropdown .searching {
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
|
||||
#search-dropdown .results {
|
||||
max-height: 500px;
|
||||
}
|
||||
|
|
|
@ -37,48 +37,6 @@
|
|||
padding-top: 60px;
|
||||
}
|
||||
|
||||
// Dropdowns
|
||||
// --------------------------------------------------
|
||||
|
||||
.d-dropdown {
|
||||
width: 290px;
|
||||
margin-top: -1px;
|
||||
right: -$mobile-wrapper-padding; // Line-up with edge of screen, not edge of padding
|
||||
|
||||
// Common
|
||||
|
||||
.heading {
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
font-size: 0.857em;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
// Search
|
||||
|
||||
.searching {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
input[type='text'] {
|
||||
width: 265px;
|
||||
font-size: 1.143em;
|
||||
}
|
||||
|
||||
&#user-dropdown {
|
||||
width: 155px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 2px 8px;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-link .badge-category {
|
||||
display: none;
|
||||
}
|
||||
|
@ -86,7 +44,3 @@
|
|||
.search-link .topic-statuses .topic-status i {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.d-dropdown#search-dropdown .heading {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -5,18 +5,10 @@ acceptance("Header (Staff)", { loggedIn: true });
|
|||
test("header", () => {
|
||||
visit("/");
|
||||
|
||||
// Notifications
|
||||
click("#user-notifications");
|
||||
andThen(() => {
|
||||
var $items = $("#notifications-dropdown li");
|
||||
ok(exists($items), "is lazily populated after user opens it");
|
||||
ok($items.first().hasClass("read"), "correctly binds items' 'read' class");
|
||||
});
|
||||
|
||||
// User dropdown
|
||||
click("#current-user");
|
||||
andThen(() => {
|
||||
ok(exists("#user-dropdown:visible"), "is lazily rendered after user opens it");
|
||||
ok(exists("#user-dropdown .user-dropdown-links"), "has showing / hiding user-dropdown links correctly bound");
|
||||
ok(exists(".user-menu:visible"), "is lazily rendered after user opens it");
|
||||
ok(exists(".user-menu .menu-links"), "has showing / hiding user-dropdown links correctly bound");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,19 +18,16 @@ componentTest('as a dropdown', {
|
|||
|
||||
test(assert) {
|
||||
assert.ok(exists(".menu-panel.hidden"), "hidden by default");
|
||||
assert.ok(!exists(".menu-selected.active"), "does not mark anything as active");
|
||||
|
||||
this.set('panelVisible', true);
|
||||
andThen(() => {
|
||||
assert.ok(!exists('.menu-panel .close-panel'), "the close X is not shown");
|
||||
assert.ok(!exists(".menu-panel.hidden"), "toggling visible makes it appear");
|
||||
assert.ok(exists(".menu-selected.active"), "marks the panel as active");
|
||||
});
|
||||
|
||||
click('#outside-area')
|
||||
andThen(() => {
|
||||
assert.ok(exists(".menu-panel.hidden"), "clicking the body hides the menu");
|
||||
assert.ok(!exists(".menu-selected.active"), "removes the active class");
|
||||
assert.equal(this.get('panelVisible'), false, 'it updates the bound variable');
|
||||
});
|
||||
}
|
||||
|
@ -52,12 +49,10 @@ componentTest('as a slide-in', {
|
|||
|
||||
test(assert) {
|
||||
assert.ok(exists(".menu-panel.hidden"), "hidden by default");
|
||||
assert.ok(!exists(".menu-selected.active"), "does not mark anything as active");
|
||||
|
||||
this.set('panelVisible', true);
|
||||
andThen(() => {
|
||||
assert.ok(!exists(".menu-panel.hidden"), "toggling visible makes it appear");
|
||||
assert.ok(!exists(".menu-selected.active"), "slide ins don't mark as active");
|
||||
});
|
||||
|
||||
click('#outside-area')
|
||||
|
|
|
@ -2,7 +2,7 @@ import { blank, present } from 'helpers/qunit-helpers';
|
|||
|
||||
moduleFor('controller:topic', 'controller:topic', {
|
||||
needs: ['controller:header', 'controller:modal', 'controller:composer', 'controller:quote-button',
|
||||
'controller:search', 'controller:topic-progress', 'controller:application']
|
||||
'controller:topic-progress', 'controller:application']
|
||||
});
|
||||
|
||||
import Topic from 'discourse/models/topic';
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
moduleFor("controller:user-dropdown");
|
||||
|
||||
test("showAdminLinks", function() {
|
||||
const currentUser = Ember.Object.create({ staff: true });
|
||||
const controller = this.subject({ currentUser });
|
||||
equal(controller.get("showAdminLinks"), true, "is true when current user is a staff member");
|
||||
|
||||
currentUser.set("staff", false);
|
||||
equal(controller.get("showAdminLinks"), false, "is false when current user is not a staff member");
|
||||
});
|
Loading…
Reference in New Issue