UX: add topic admin button to the bottom of the topic

This commit is contained in:
Régis Hanol 2014-10-06 20:19:07 +02:00
parent b1271ed44b
commit 67c10a7eab
12 changed files with 167 additions and 98 deletions

View file

@ -0,0 +1,24 @@
export default Em.Component.extend({
tagName: "button",
classNames: ["btn", "no-text", "show-topic-admin"],
attributeBindings: ["title"],
title: I18n.t("topic_admin_menu"),
render: function(buffer) {
buffer.push("<i class='fa fa-wrench'></i>");
},
click: function() {
var $target = this.$(),
position = $target.position(),
width = $target.innerWidth();
var location = {
position: "fixed",
left: position.left + width,
top: position.top,
};
this.appEvents.trigger("topic-admin-menu:open", location);
this.sendAction("show");
return false;
}
});

View file

@ -10,17 +10,11 @@ import ObjectController from 'discourse/controllers/object';
**/ **/
export default ObjectController.extend({ export default ObjectController.extend({
menuVisible: false, menuVisible: false,
needs: ['modal'], showRecover: Em.computed.and('deleted', 'details.can_recover'),
actions: { actions: {
show: function() { show: function() { this.set('menuVisible', true); },
this.set('menuVisible', true); hide: function() { this.set('menuVisible', false); }
}, }
hide: function() {
this.set('menuVisible', false);
}
},
showRecover: Em.computed.and('deleted', 'details.can_recover')
}); });

View file

@ -13,6 +13,10 @@ Discourse.TopicRoute = Discourse.Route.extend({
}, },
actions: { actions: {
showTopicAdminMenu: function() {
this.controllerFor("topic-admin-menu").send("show");
},
// Modals that can pop up within a topic // Modals that can pop up within a topic
expandPostUser: function(post) { expandPostUser: function(post) {
this.controllerFor('user-expansion').show(post.get('username'), post.get('uploaded_avatar_id')); this.controllerFor('user-expansion').show(post.get('username'), post.get('uploaded_avatar_id'));

View file

@ -1,74 +1,68 @@
{{#if menuVisible}} <h3>{{i18n admin_title}}</h3>
<div class="topic-admin-menu">
<h3>{{i18n admin_title}}</h3>
<ul> <ul>
<li> <li>
<button {{action "toggleMultiSelect"}} class='btn btn-admin'><i class='fa fa-tasks'></i> {{i18n topic.actions.multi_select}}</button> <button {{action "toggleMultiSelect"}} class='btn btn-admin'>{{fa-icon tasks}} {{i18n topic.actions.multi_select}}</button>
</li> </li>
{{#if details.can_delete}} {{#if details.can_delete}}
<li> <li>
<button {{action "deleteTopic"}} class='btn btn-admin btn-danger'><i class='fa fa-trash-o'></i> {{i18n topic.actions.delete}}</button> <button {{action "deleteTopic"}} class='btn btn-admin btn-danger'>{{fa-icon trash-o}} {{i18n topic.actions.delete}}</button>
</li> </li>
{{/if}} {{/if}}
{{#if showRecover}} {{#if showRecover}}
<li> <li>
<button {{action "recoverTopic"}} class='btn btn-admin'><i class='fa fa-undo'></i> {{i18n topic.actions.recover}}</button> <button {{action "recoverTopic"}} class='btn btn-admin'>{{fa-icon undo}} {{i18n topic.actions.recover}}</button>
</li> </li>
{{/if}} {{/if}}
<li> <li>
{{#if closed}} {{#if closed}}
<button {{action "toggleClosed"}} class='btn btn-admin'><i class='fa fa-unlock'></i> {{i18n topic.actions.open}}</button> <button {{action "toggleClosed"}} class='btn btn-admin'>{{fa-icon unlock}} {{i18n topic.actions.open}}</button>
{{else}} {{else}}
<button {{action "toggleClosed"}} class='btn btn-admin'><i class='fa fa-lock'></i> {{i18n topic.actions.close}}</button> <button {{action "toggleClosed"}} class='btn btn-admin'>{{fa-icon lock}} {{i18n topic.actions.close}}</button>
<button {{action "showAutoClose"}} class='btn btn-admin'><i class='fa fa-clock-o'></i> {{i18n topic.actions.auto_close}}</button> <button {{action "showAutoClose"}} class='btn btn-admin'>{{fa-icon clock-o}} {{i18n topic.actions.auto_close}}</button>
{{/if}} {{/if}}
</li> </li>
{{#unless isPrivateMessage}} {{#unless isPrivateMessage}}
<li> <li>
{{#if isBanner}} {{#if isBanner}}
<button {{action "removeBanner"}} class='btn btn-admin'><i class='fa fa-bullhorn'></i> {{i18n topic.actions.remove_banner}}</button> <button {{action "removeBanner"}} class='btn btn-admin'>{{fa-icon bullhorn}} {{i18n topic.actions.remove_banner}}</button>
{{else}} {{else}}
{{#if visible}}
<button {{action "makeBanner"}} class='btn btn-admin'><i class='fa fa-bullhorn'></i> {{i18n topic.actions.make_banner}}</button>
{{/if}}
{{/if}}
</li>
<li>
{{#if pinned_at}}
<button {{action "togglePinned"}} class='btn btn-admin'><i class='fa fa-thumb-tack'></i> {{i18n topic.actions.unpin}}</button>
{{else}}
{{#if visible}}
<button {{action "togglePinned"}} class='btn btn-admin'><i class='fa fa-thumb-tack'></i> {{i18n topic.actions.pin}}</button>
<button {{action "togglePinnedGlobally"}} class='btn btn-admin'><i class='fa fa-thumb-tack'></i> {{i18n topic.actions.pin_globally}}</button>
{{/if}}
{{/if}}
</li>
{{/unless}}
<li>
{{#if archived}}
<button {{action "toggleArchived"}} class='btn btn-admin'><i class='fa fa-folder'></i> {{i18n topic.actions.unarchive}}</button>
{{else}}
<button {{action "toggleArchived"}} class='btn btn-admin'><i class='fa fa-folder'></i> {{i18n topic.actions.archive}}</button>
{{/if}}
</li>
<li>
{{#if visible}} {{#if visible}}
<button {{action "toggleVisibility"}} class='btn btn-admin'><i class='fa fa-eye-slash'></i> {{i18n topic.actions.invisible}}</button> <button {{action "makeBanner"}} class='btn btn-admin'>{{fa-icon bullhorn}} {{i18n topic.actions.make_banner}}</button>
{{else}}
<button {{action "toggleVisibility"}} class='btn btn-admin'><i class='fa fa-eye'></i> {{i18n topic.actions.visible}}</button>
{{/if}} {{/if}}
</li> {{/if}}
</li>
</ul> <li>
</div> {{#if pinned_at}}
{{else}} <button {{action "togglePinned"}} class='btn btn-admin'>{{fa-icon thumb-tack}} {{i18n topic.actions.unpin}}</button>
<button class='btn no-text' id='show-topic-admin' {{action "show"}}><i class='fa fa-wrench'></i></button> {{else}}
{{/if}} {{#if visible}}
<button {{action "togglePinned"}} class='btn btn-admin'>{{fa-icon thumb-tack}} {{i18n topic.actions.pin}}</button>
<button {{action "togglePinnedGlobally"}} class='btn btn-admin'>{{fa-icon thumb-tack}} {{i18n topic.actions.pin_globally}}</button>
{{/if}}
{{/if}}
</li>
{{/unless}}
<li>
{{#if archived}}
<button {{action "toggleArchived"}} class='btn btn-admin'>{{fa-icon folder}} {{i18n topic.actions.unarchive}}</button>
{{else}}
<button {{action "toggleArchived"}} class='btn btn-admin'>{{fa-icon folder}} {{i18n topic.actions.archive}}</button>
{{/if}}
</li>
<li>
{{#if visible}}
<button {{action "toggleVisibility"}} class='btn btn-admin'>{{fa-icon eye-slash}} {{i18n topic.actions.invisible}}</button>
{{else}}
<button {{action "toggleVisibility"}} class='btn btn-admin'>{{fa-icon eye}} {{i18n topic.actions.visible}}</button>
{{/if}}
</li>
</ul>

View file

@ -25,12 +25,12 @@
{{/if}} {{/if}}
{{text-field id='edit-title' value=newTitle maxLength=maxTitleLength}} {{text-field id='edit-title' value=newTitle maxLength=maxTitleLength}}
<button class='btn btn-primary btn-small no-text' {{action "finishedEditingTopic"}}><i class='fa fa-check'></i></button> <button class='btn btn-primary btn-small no-text' {{action "finishedEditingTopic"}}>{{fa-icon check}}</button>
<button class='btn btn-small no-text' {{action "cancelEditingTopic"}}><i class='fa fa-times'></i></button> <button class='btn btn-small no-text' {{action "cancelEditingTopic"}}>{{fa-icon times}}</button>
{{else}} {{else}}
<h1> <h1>
{{#unless is_warning}} {{#unless is_warning}}
<span class="private-message-glyph"><i class='fa fa-envelope'></i></span> <span class="private-message-glyph">{{fa-icon envelope}}</span>
{{/unless}} {{/unless}}
{{#if details.loaded}} {{#if details.loaded}}
@ -46,7 +46,7 @@
{{#if details.can_edit}} {{#if details.can_edit}}
<a href='#' {{action "editTopic"}} class='edit-topic' title='{{i18n edit}}'><i class="fa fa-pencil"></i></a> <a href='#' {{action "editTopic"}} class='edit-topic' title='{{i18n edit}}'>{{fa-icon pencil}}</a>
{{/if}} {{/if}}
</h1> </h1>
@ -118,7 +118,6 @@
{{/if}} {{/if}}
{{/if}} {{/if}}
</section> </section>
</div> </div>
@ -134,7 +133,7 @@
{{message}} {{message}}
{{#if noRetry}} {{#if noRetry}}
{{#unless currentUser}} {{#unless currentUser}}
<button {{action "showLogin"}} class='btn btn-primary topic-retry'><i class="fa fa-user"></i>{{i18n log_in}}</button> <button {{action "showLogin"}} class='btn btn-primary topic-retry'>{{fa-icon user}} {{i18n log_in}}</button>
{{/unless}} {{/unless}}
{{else}} {{else}}
<button class="btn btn-primary topic-retry" {{action "retryLoading"}}>{{i18n errors.buttons.again}}</button> <button class="btn btn-primary topic-retry" {{action "retryLoading"}}>{{i18n errors.buttons.again}}</button>
@ -159,5 +158,6 @@
{{/if}} {{/if}}
{{#if currentUser.canManageTopic}} {{#if currentUser.canManageTopic}}
{{show-topic-admin show="showTopicAdminMenu"}}
{{render "topic-admin-menu"}} {{render "topic-admin-menu"}}
{{/if}} {{/if}}

View file

@ -5,9 +5,10 @@ export default Discourse.View.extend({
title: function() { title: function() {
return I18n.t(this.get('helpKey') || this.get('textKey')); return I18n.t(this.get('helpKey') || this.get('textKey'));
}.property('helpKey'), }.property('helpKey', 'textKey'),
text: function() { text: function() {
if (Em.empty(this.get('textKey'))) { return ""; }
return I18n.t(this.get('textKey')); return I18n.t(this.get('textKey'));
}.property('textKey'), }.property('textKey'),

View file

@ -0,0 +1,21 @@
import ButtonView from "discourse/views/button";
export default ButtonView.extend({
classNameBindings: [":no-text"],
helpKey: "topic_admin_menu",
renderIcon: function(buffer) {
buffer.push("<i class='fa fa-wrench'></i>");
},
click: function() {
var offset = this.$().offset();
var location = {
position: "absolute",
left: offset.left,
top: offset.top,
};
this.get("controller").appEvents.trigger("topic-admin-menu:open", location);
return this.get("controller").send("showTopicAdminMenu");
}
});

View file

@ -7,21 +7,47 @@
@module Discourse @module Discourse
**/ **/
export default Discourse.View.extend({ export default Discourse.View.extend({
classNameBindings: ["controller.menuVisible::hidden", ":topic-admin-menu"],
willDestroyElement: function() { _setup: function() {
$('html').off('mouseup.discourse-topic-admin-menu');
},
didInsertElement: function() {
var self = this; var self = this;
$('html').on('mouseup.discourse-topic-admin-menu', function(e) {
this.appEvents.on("topic-admin-menu:open", this, "_changeLocation");
$("html").on("mouseup.discourse-topic-admin-menu", function(e) {
var $target = $(e.target); var $target = $(e.target);
if ($target.is('button') || self.$().has($target).length === 0) { if ($target.is("button") || self.$().has($target).length === 0) {
self.get('controller').send('hide'); self.get("controller").send("hide");
} }
}); });
} }.on("didInsertElement"),
_changeLocation: function(location) {
console.log("_changeLocation", location);
var $this = this.$();
switch (location.position) {
case "absolute": {
$this.css({
position: "absolute",
top: location.top - $this.innerHeight() + 5,
left: location.left,
})
break;
}
case "fixed": {
$this.css({
position: "fixed",
top: location.top,
left: location.left - $this.innerWidth(),
})
break;
}
}
},
_cleanup: function() {
$("html").off("mouseup.discourse-topic-admin-menu");
this.appEvents.off("topic-admin-menu:open");
}.on("willDestroyElement"),
}); });

View file

@ -1,3 +1,4 @@
import TopicAdminMenuButton from 'discourse/views/topic-admin-menu-button';
import LoginReplyButton from 'discourse/views/login-reply-button'; import LoginReplyButton from 'discourse/views/login-reply-button';
import FlagTopicButton from 'discourse/views/flag-topic-button'; import FlagTopicButton from 'discourse/views/flag-topic-button';
import StarButton from 'discourse/views/star-button'; import StarButton from 'discourse/views/star-button';
@ -21,6 +22,9 @@ export default DiscourseContainerView.extend({
createButtons: function() { createButtons: function() {
var topic = this.get('topic'); var topic = this.get('topic');
if (Discourse.User.current()) { if (Discourse.User.current()) {
if (Discourse.User.currentProp("staff")) {
this.attachViewClass(TopicAdminMenuButton);
}
if (!topic.get('isPrivateMessage')) { if (!topic.get('isPrivateMessage')) {
// We hide some controls from private messages // We hide some controls from private messages
if (this.get('topic.details.can_invite_to')) { if (this.get('topic.details.can_invite_to')) {

View file

@ -1,10 +1,11 @@
// Styles for the topic admin menu // Styles for the topic admin menu
#show-topic-admin { .show-topic-admin {
position: fixed; position: fixed;
top: 70px; top: 70px;
right: 10px; right: 10px;
z-index: 1000; z-index: 1000;
outline: 0;
} }
.topic-admin-menu { .topic-admin-menu {
@ -12,9 +13,6 @@
width: 205px; width: 205px;
padding: 10px; padding: 10px;
border: 1px solid scale-color-diff(); border: 1px solid scale-color-diff();
position: fixed;
top: 70px;
right: 10px;
z-index: 1001; z-index: 1001;
ul { ul {

View file

@ -473,6 +473,7 @@ a.star {
color: $primary; color: $primary;
} }
.btn { .btn {
outline: 0;
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 10px; margin-right: 10px;
.fa-star {margin-right: 5px;} .fa-star {margin-right: 5px;}

View file

@ -101,6 +101,8 @@ en:
google+: 'share this link on Google+' google+: 'share this link on Google+'
email: 'send this link in an email' email: 'send this link in an email'
topic_admin_menu: "topic admin actions"
edit: 'edit the title and category of this topic' edit: 'edit the title and category of this topic'
not_implemented: "That feature hasn't been implemented yet, sorry!" not_implemented: "That feature hasn't been implemented yet, sorry!"
no_value: "No" no_value: "No"