mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
FEATURE: Move topic admin button into timeline
Also includes converting the topic admin menu to the widget framework.
This commit is contained in:
parent
c0e4d0f6f2
commit
1643ff0f3c
11 changed files with 221 additions and 128 deletions
|
@ -21,6 +21,8 @@ export default Ember.Component.extend({
|
||||||
this._super();
|
this._super();
|
||||||
const name = this.get('widget');
|
const name = this.get('widget');
|
||||||
|
|
||||||
|
(this.get('delegated') || []).forEach(m => this.set(m, m));
|
||||||
|
|
||||||
this._widgetClass = queryRegistry(name) || this.container.lookupFactory(`widget:${name}`);
|
this._widgetClass = queryRegistry(name) || this.container.lookupFactory(`widget:${name}`);
|
||||||
|
|
||||||
if (!this._widgetClass) {
|
if (!this._widgetClass) {
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -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');
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,16 +1,15 @@
|
||||||
import ContainerView from 'discourse/views/container';
|
import ContainerView from 'discourse/views/container';
|
||||||
import { on } from 'ember-addons/ember-computed-decorators';
|
|
||||||
|
|
||||||
export default ContainerView.extend({
|
export default ContainerView.extend({
|
||||||
elementId: 'topic-footer-buttons',
|
elementId: 'topic-footer-buttons',
|
||||||
|
|
||||||
@on('init')
|
init() {
|
||||||
createButtons() {
|
this._super();
|
||||||
const topic = this.get('topic');
|
|
||||||
const currentUser = this.get('controller.currentUser');
|
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, 'topic-footer-main-buttons');
|
||||||
this.attachViewWithArgs(viewArgs, 'pinned-button');
|
this.attachViewWithArgs(viewArgs, 'pinned-button');
|
||||||
this.attachViewWithArgs(viewArgs, 'topic-notifications-button');
|
this.attachViewWithArgs(viewArgs, 'topic-notifications-button');
|
||||||
|
|
|
@ -17,13 +17,28 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
||||||
selectedPosts: null,
|
selectedPosts: null,
|
||||||
selectedReplies: null,
|
selectedReplies: null,
|
||||||
queryParams: ['filter', 'username_filters', 'show_deleted'],
|
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,
|
enteredAt: null,
|
||||||
retrying: false,
|
retrying: false,
|
||||||
adminMenuVisible: false,
|
|
||||||
|
|
||||||
showRecover: Em.computed.and('model.deleted', 'model.details.can_recover'),
|
topicDelegated: [
|
||||||
isFeatured: Em.computed.or("model.pinned_at", "model.isBanner"),
|
'toggleMultiSelect',
|
||||||
|
'deleteTopic',
|
||||||
|
'recoverTopic',
|
||||||
|
'toggleClosed',
|
||||||
|
'showAutoClose',
|
||||||
|
'showFeatureTopic',
|
||||||
|
'showChangeTimestamp',
|
||||||
|
'toggleArchived',
|
||||||
|
'toggleVisibility',
|
||||||
|
'convertToPublicTopic',
|
||||||
|
'convertToPrivateMessage',
|
||||||
|
'jumpTop',
|
||||||
|
'jumpToPost',
|
||||||
|
'jumpToIndex',
|
||||||
|
'jumpBottom',
|
||||||
|
'replyToPost'
|
||||||
|
],
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
showTimeline() {
|
showTimeline() {
|
||||||
|
@ -237,14 +252,6 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
||||||
return this.get('model.details').removeAllowedUser(user);
|
return this.get('model.details').removeAllowedUser(user);
|
||||||
},
|
},
|
||||||
|
|
||||||
showTopicAdminMenu() {
|
|
||||||
this.set('adminMenuVisible', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
hideTopicAdminMenu() {
|
|
||||||
this.set('adminMenuVisible', false);
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteTopic() {
|
deleteTopic() {
|
||||||
this.deleteTopic();
|
this.deleteTopic();
|
||||||
},
|
},
|
||||||
|
|
|
@ -72,19 +72,10 @@
|
||||||
<div class="posts-wrapper">
|
<div class="posts-wrapper">
|
||||||
|
|
||||||
{{#if showTimeline}}
|
{{#if showTimeline}}
|
||||||
{{topic-timeline topic=model
|
{{topic-timeline topic=model loading=model.postStream.loading delegated=topicDelegated}}
|
||||||
loading=model.postStream.loading
|
|
||||||
jumpTop="jumpTop"
|
|
||||||
jumpToPost="jumpToPost"
|
|
||||||
jumpToIndex="jumpToIndex"
|
|
||||||
jumpBottom="jumpBottom"
|
|
||||||
replyToPost="replyToPost"}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
{{topic-progress topic=model
|
{{topic-progress topic=model delegated=topicDelegated}}
|
||||||
jumpTop="jumpTop"
|
{{topic-admin-menu-button topic=model fixed="true" delegated=topicDelegated}}
|
||||||
jumpToPost="jumpToPost"
|
|
||||||
jumpToIndex="jumpToIndex"
|
|
||||||
jumpBottom="jumpBottom"}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
|
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
|
||||||
|
|
||||||
|
@ -136,7 +127,7 @@
|
||||||
{{! replace "Log In to Reply" with the infobox }}
|
{{! replace "Log In to Reply" with the infobox }}
|
||||||
{{signup-cta}}
|
{{signup-cta}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{topic-footer-buttons topic=model}}
|
{{topic-footer-buttons topic=model topicDelegated=topicDelegated}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if model.pending_posts_count}}
|
{{#if model.pending_posts_count}}
|
||||||
|
@ -212,78 +203,3 @@
|
||||||
{{render "quote-button"}}
|
{{render "quote-button"}}
|
||||||
{{/if}}
|
{{/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}}
|
|
||||||
|
|
|
@ -8,12 +8,13 @@ export default ContainerView.extend({
|
||||||
createButtons() {
|
createButtons() {
|
||||||
const mobileView = this.site.mobileView;
|
const mobileView = this.site.mobileView;
|
||||||
|
|
||||||
|
const topic = this.get('topic');
|
||||||
|
|
||||||
if (!mobileView && this.currentUser.get('staff')) {
|
if (!mobileView && this.currentUser.get('staff')) {
|
||||||
const viewArgs = {action: 'showTopicAdminMenu', title: 'topic_admin_menu', icon: 'wrench', position: 'absolute'};
|
const viewArgs = { topic, delegated: this.get('topicDelegated') };
|
||||||
this.attachViewWithArgs(viewArgs, 'show-popup-button');
|
this.attachViewWithArgs(viewArgs, 'topic-admin-menu-button');
|
||||||
}
|
}
|
||||||
|
|
||||||
const topic = this.get('topic');
|
|
||||||
if (!topic.get('isPrivateMessage')) {
|
if (!topic.get('isPrivateMessage')) {
|
||||||
if (mobileView) {
|
if (mobileView) {
|
||||||
this.attachViewWithArgs({ topic }, 'topic-footer-mobile-dropdown');
|
this.attachViewWithArgs({ topic }, 'topic-footer-mobile-dropdown');
|
||||||
|
|
|
@ -40,11 +40,18 @@ export default createWidget('button', {
|
||||||
return contents;
|
return contents;
|
||||||
},
|
},
|
||||||
|
|
||||||
click() {
|
click(e) {
|
||||||
const attrs = this.attrs;
|
const attrs = this.attrs;
|
||||||
if (attrs.disabled) { return; }
|
if (attrs.disabled) { return; }
|
||||||
|
|
||||||
$(`button.widget-button`).removeClass('d-hover').blur();
|
$(`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);
|
return this.sendWidgetAction(attrs.action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
160
app/assets/javascripts/discourse/widgets/topic-admin-menu.js.es6
Normal file
160
app/assets/javascripts/discourse/widgets/topic-admin-menu.js.es6
Normal 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');
|
||||||
|
}
|
||||||
|
});
|
|
@ -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 result = [ h('div.timeline-controls', controls) ];
|
||||||
const stream = attrs.topic.get('postStream.stream');
|
const stream = attrs.topic.get('postStream.stream');
|
||||||
if (stream.length > 2) {
|
if (stream.length > 2) {
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
.timeline-controls {
|
.timeline-controls {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.start-date {
|
.start-date {
|
||||||
|
|
Loading…
Reference in a new issue