FEATURE: Move topic admin button into timeline

Also includes converting the topic admin menu to the widget framework.
This commit is contained in:
Robin Ward 2016-05-20 14:47:25 -04:00
parent c0e4d0f6f2
commit 1643ff0f3c
No known key found for this signature in database
GPG key ID: 0E091E2B4ED1B83D
11 changed files with 221 additions and 128 deletions

View file

@ -21,6 +21,8 @@ export default Ember.Component.extend({
this._super();
const name = this.get('widget');
(this.get('delegated') || []).forEach(m => this.set(m, m));
this._widgetClass = queryRegistry(name) || this.container.lookupFactory(`widget:${name}`);
if (!this._widgetClass) {

View file

@ -1,17 +0,0 @@
import DButton from 'discourse/components/d-button';
export default DButton.extend({
click() {
const $target = this.$(),
position = $target.position(),
width = $target.innerWidth(),
loc = {
position: this.get('position') || "fixed",
left: position.left + width,
top: position.top
};
this.appEvents.trigger("popup-menu:open", loc);
this.sendAction("action");
}
});

View file

@ -0,0 +1,10 @@
import MountWidget from 'discourse/components/mount-widget';
export default MountWidget.extend({
tagName: 'span',
widget: "topic-admin-menu-button",
buildArgs() {
return this.getProperties('topic', 'fixed');
}
});

View file

@ -1,16 +1,15 @@
import ContainerView from 'discourse/views/container';
import { on } from 'ember-addons/ember-computed-decorators';
export default ContainerView.extend({
elementId: 'topic-footer-buttons',
@on('init')
createButtons() {
const topic = this.get('topic');
const currentUser = this.get('controller.currentUser');
init() {
this._super();
if (this.currentUser) {
const viewArgs = this.getProperties('topic', 'topicDelegated');
viewArgs.currentUser = this.currentUser;
if (currentUser) {
const viewArgs = { topic, currentUser };
this.attachViewWithArgs(viewArgs, 'topic-footer-main-buttons');
this.attachViewWithArgs(viewArgs, 'pinned-button');
this.attachViewWithArgs(viewArgs, 'topic-notifications-button');

View file

@ -17,13 +17,28 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
selectedPosts: null,
selectedReplies: null,
queryParams: ['filter', 'username_filters', 'show_deleted'],
loadedAllPosts: Em.computed.or('model.postStream.loadedAllPosts', 'model.postStream.loadingLastPost'),
loadedAllPosts: Ember.computed.or('model.postStream.loadedAllPosts', 'model.postStream.loadingLastPost'),
enteredAt: null,
retrying: false,
adminMenuVisible: false,
showRecover: Em.computed.and('model.deleted', 'model.details.can_recover'),
isFeatured: Em.computed.or("model.pinned_at", "model.isBanner"),
topicDelegated: [
'toggleMultiSelect',
'deleteTopic',
'recoverTopic',
'toggleClosed',
'showAutoClose',
'showFeatureTopic',
'showChangeTimestamp',
'toggleArchived',
'toggleVisibility',
'convertToPublicTopic',
'convertToPrivateMessage',
'jumpTop',
'jumpToPost',
'jumpToIndex',
'jumpBottom',
'replyToPost'
],
@computed
showTimeline() {
@ -237,14 +252,6 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
return this.get('model.details').removeAllowedUser(user);
},
showTopicAdminMenu() {
this.set('adminMenuVisible', true);
},
hideTopicAdminMenu() {
this.set('adminMenuVisible', false);
},
deleteTopic() {
this.deleteTopic();
},

View file

@ -72,19 +72,10 @@
<div class="posts-wrapper">
{{#if showTimeline}}
{{topic-timeline topic=model
loading=model.postStream.loading
jumpTop="jumpTop"
jumpToPost="jumpToPost"
jumpToIndex="jumpToIndex"
jumpBottom="jumpBottom"
replyToPost="replyToPost"}}
{{topic-timeline topic=model loading=model.postStream.loading delegated=topicDelegated}}
{{else}}
{{topic-progress topic=model
jumpTop="jumpTop"
jumpToPost="jumpToPost"
jumpToIndex="jumpToIndex"
jumpBottom="jumpBottom"}}
{{topic-progress topic=model delegated=topicDelegated}}
{{topic-admin-menu-button topic=model fixed="true" delegated=topicDelegated}}
{{/if}}
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
@ -136,7 +127,7 @@
{{! replace "Log In to Reply" with the infobox }}
{{signup-cta}}
{{else}}
{{topic-footer-buttons topic=model}}
{{topic-footer-buttons topic=model topicDelegated=topicDelegated}}
{{/if}}
{{#if model.pending_posts_count}}
@ -212,78 +203,3 @@
{{render "quote-button"}}
{{/if}}
{{#if currentUser.canManageTopic}}
{{show-popup-button action="showTopicAdminMenu" class="show-topic-admin" title="topic_admin_menu" icon="wrench"}}
{{#popup-menu visible=adminMenuVisible hide="hideTopicAdminMenu" title="admin_title" extraClasses="topic-admin-popup-menu"}}
<li class="topic-admin-multi-select">
{{d-button action="toggleMultiSelect" icon="tasks" label="topic.actions.multi_select"}}
</li>
{{#if model.details.can_delete}}
<li class="topic-admin-delete">
{{d-button action="deleteTopic" icon="trash-o" label="topic.actions.delete" class="btn-danger"}}
</li>
{{/if}}
{{#if showRecover}}
<li class="topic-admin-recover">
{{d-button action="recoverTopic" icon="undo" label="topic.actions.recover"}}
</li>
{{/if}}
<li class="topic-admin-autoclose">
{{#if model.closed}}
{{d-button action="toggleClosed" icon="unlock" label="topic.actions.open"}}
{{else}}
{{d-button action="toggleClosed" icon="lock" label="topic.actions.close"}}
{{d-button action="showAutoClose" icon="clock-o" label="topic.actions.auto_close"}}
{{/if}}
</li>
{{#unless model.isPrivateMessage}}
{{#if model.visible}}
<li class="topic-admin-pin">
{{#if isFeatured}}
{{d-button action="showFeatureTopic" icon="thumb-tack" label="topic.actions.unpin"}}
{{else}}
{{d-button action="showFeatureTopic" icon="thumb-tack" label="topic.actions.pin"}}
{{/if}}
</li>
{{/if}}
{{/unless}}
<li class="topic-admin-change-timestamp">
{{d-button action="showChangeTimestamp" icon="calendar" label="topic.change_timestamp.title"}}
</li>
<li class="topic-admin-archive">
{{#if model.archived}}
{{d-button action="toggleArchived" icon="folder" label="topic.actions.unarchive"}}
{{else}}
{{#unless model.isPrivateMessage}}
{{d-button action="toggleArchived" icon="folder" label="topic.actions.archive"}}
{{/unless}}
{{/if}}
</li>
<li class="topic-admin-visible">
{{#if model.visible}}
{{d-button action="toggleVisibility" icon="eye-slash" label="topic.actions.invisible"}}
{{else}}
{{d-button action="toggleVisibility" icon="eye" label="topic.actions.visible"}}
{{/if}}
</li>
{{#if currentUser.staff}}
<li class="topic-admin-convert">
{{#if model.isPrivateMessage}}
{{d-button action="convertToPublicTopic" icon="comment" label="topic.actions.make_public"}}
{{else}}
{{d-button action="convertToPrivateMessage" icon="envelope" label="topic.actions.make_private"}}
{{/if}}
</li>
{{/if}}
{{plugin-outlet "topic-admin-menu-buttons"}}
{{/popup-menu}}
{{/if}}

View file

@ -8,12 +8,13 @@ export default ContainerView.extend({
createButtons() {
const mobileView = this.site.mobileView;
const topic = this.get('topic');
if (!mobileView && this.currentUser.get('staff')) {
const viewArgs = {action: 'showTopicAdminMenu', title: 'topic_admin_menu', icon: 'wrench', position: 'absolute'};
this.attachViewWithArgs(viewArgs, 'show-popup-button');
const viewArgs = { topic, delegated: this.get('topicDelegated') };
this.attachViewWithArgs(viewArgs, 'topic-admin-menu-button');
}
const topic = this.get('topic');
if (!topic.get('isPrivateMessage')) {
if (mobileView) {
this.attachViewWithArgs({ topic }, 'topic-footer-mobile-dropdown');

View file

@ -40,11 +40,18 @@ export default createWidget('button', {
return contents;
},
click() {
click(e) {
const attrs = this.attrs;
if (attrs.disabled) { return; }
$(`button.widget-button`).removeClass('d-hover').blur();
if (attrs.secondaryAction) {
this.sendWidgetAction(attrs.secondaryAction);
}
if (attrs.sendActionEvent) {
return this.sendWidgetAction(attrs.action, e);
}
return this.sendWidgetAction(attrs.action);
}
});

View file

@ -0,0 +1,160 @@
import { createWidget, applyDecorators } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
createWidget('admin-menu-button', {
html(attrs) {
let className = 'btn';
if (attrs.buttonClass) { className += ' ' + attrs.buttonClass; }
return h('li', { className: attrs.className }, this.attach('button', {
className,
action: attrs.action,
icon: attrs.icon,
label: `topic.${attrs.label}`,
secondaryAction: 'hideAdminMenu'
}));
}
});
createWidget('topic-admin-menu-button', {
tagName: 'span',
buildKey: () => `topic-admin-menu-button`,
defaultState() {
return { expanded: false, position: null };
},
html(attrs, state) {
if (!this.currentUser || !this.currentUser.get('canManageTopic')) { return; }
const result = [];
result.push(this.attach('button', {
className: 'btn no-text' + (attrs.fixed ? " show-topic-admin" : ""),
title: 'topic_admin_menu',
icon: 'wrench',
action: 'showAdminMenu',
sendActionEvent: true
}));
if (state.expanded) {
result.push(this.attach('topic-admin-menu', { position: state.position,
fixed: attrs.fixed,
topic: attrs.topic }));
}
return result;
},
hideAdminMenu() {
this.state.expanded = false;
this.state.position = null;
},
showAdminMenu(e) {
this.state.expanded = true;
const $button = $(e.target).closest('button');
const position = $button.position();
position.left = position.left;
if (this.attrs.fixed) {
position.left += $button.width() - 203;
}
this.state.position = position;
}
});
export default createWidget('topic-admin-menu', {
tagName: 'div.popup-menu.topic-admin-popup-menu',
buildAttributes(attrs) {
const { top, left } = attrs.position;
const position = attrs.fixed ? 'fixed' : 'absolute';
return { style: `position: ${position}; top: ${top}px; left: ${left}px;` };
},
html(attrs) {
const buttons = [];
buttons.push({ className: 'topic-admin-multi-select',
action: 'toggleMultiSelect',
icon: 'tasks',
label: 'actions.multi_select' });
const topic = attrs.topic;
const details = topic.get('details');
if (details.get('can_delete')) {
buttons.push({ className: 'topic-admin-delete',
buttonClass: 'btn-danger',
action: 'deleteTopic',
icon: 'trash-o',
label: 'actions.delete' });
}
if (topic.get('deleted') && details.get('can_recover')) {
buttons.push({ className: 'topic-admin-recover',
action: 'recoverTopic',
icon: 'undo',
label: 'actions.recover' });
}
if (topic.get('closed')) {
buttons.push({ className: 'topic-admin-open',
action: 'toggleClosed',
icon: 'unlock',
label: 'actions.open' });
} else {
buttons.push({ className: 'topic-admin-close',
action: 'toggleClosed',
icon: 'lock',
label: 'actions.close' });
buttons.push({ className: 'topic-admin-autoclose',
action: 'showAutoClose',
icon: 'clock-o',
label: 'actions.auto_close' });
}
const isPrivateMessage = topic.get('isPrivateMessage');
if (!isPrivateMessage && topic.get('visible')) {
const featured = topic.get('pinned_at') || topic.get('isBanner');
buttons.push({ className: 'topic-admin-pin',
action: 'showFeatureTopic',
icon: 'thumb-tack',
label: featured ? 'actions.unpin' : 'actions.pin' });
}
buttons.push({ className: 'topic-admin-change-timestamp',
action: 'showChangeTimestamp',
icon: 'calendar',
label: 'change_timestamp.title' });
if (!isPrivateMessage) {
buttons.push({ className: 'topic-admin-archive',
action: 'toggleArchived',
icon: 'folder',
label: topic.get('archived') ? 'actions.unarchive' : 'actions.archive' });
}
const visible = topic.get('visible');
buttons.push({ className: 'topic-admin-visible',
action: 'toggleVisibility',
icon: visible ? 'eye' : 'eye-slash',
label: visible ? 'actions.invisible' : 'actions.visible' });
if (this.currentUser.get('staff')) {
buttons.push({ className: 'topic-admin-convert',
action: isPrivateMessage ? 'convertToPublicTopic' : 'convertToPrivateMessage',
icon: isPrivateMessage ? 'comment' : 'envelope',
label: isPrivateMessage ? 'actions.make_public' : 'actions.make_private' });
}
const extraButtons = applyDecorators(this, 'adminMenuButtons', this.attrs, this.state);
return [ h('h3', I18n.t('admin_title')),
h('ul', buttons.concat(extraButtons).map(b => this.attach('admin-menu-button', b))) ];
},
clickOutside() {
this.sendWidgetAction('hideAdminMenu');
}
});

View file

@ -210,6 +210,10 @@ export default createWidget('topic-timeline', {
}));
}
if (this.currentUser.get('canManageTopic')) {
controls.push(this.attach('topic-admin-menu-button', { topic }));
}
const result = [ h('div.timeline-controls', controls) ];
const stream = attrs.topic.get('postStream.stream');
if (stream.length > 2) {

View file

@ -19,6 +19,10 @@
.timeline-controls {
margin-bottom: 2em;
button {
margin-right: 0.5em;
}
}
.start-date {