import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse/helpers/fa-icon';
// Helper class for rendering a button
export const Button = function(action, label, icon, opts) {
this.action = action;
this.label = label;
if (typeof icon === "object") {
this.opts = icon;
} else {
this.icon = icon;
}
this.opts = this.opts || opts || {};
};
function animateHeart($elem, start, end, complete) {
if (Ember.testing) { return Ember.run(this, complete); }
$elem.stop()
.css('textIndent', start)
.animate({ textIndent: end }, {
complete,
step(now) {
$(this).css('transform','scale('+now+')');
},
duration: 150
}, 'linear');
}
Button.prototype.render = function(buffer) {
const opts = this.opts;
const label = I18n.t(this.label, opts.labelOptions);
if (opts.prefixHTML) {
buffer.push(opts.prefixHTML);
}
buffer.push("");
};
let hiddenButtons;
const PostMenuComponent = Ember.Component.extend(StringBuffer, {
tagName: 'section',
classNames: ['post-menu-area', 'clearfix'],
rerenderTriggers: [
'post.deleted_at',
'post.likeAction.count',
'post.likeAction.users.length',
'post.reply_count',
'post.showRepliesBelow',
'post.can_delete',
'post.bookmarked',
'post.shareUrl',
'post.topic.deleted_at',
'post.replies.length',
'post.wiki',
'post.post_type',
'collapsed'],
_collapsedByDefault: function() {
this.set('collapsed', true);
}.on('init'),
renderString(buffer) {
const post = this.get('post');
buffer.push("");
},
// Delegate click actions
click(e) {
const $target = $(e.target);
const action = $target.data('action') || $target.parent().data('action');
if ($target.prop('disabled') || $target.parent().prop('disabled')) { return; }
if (!action) return;
const handler = this["click" + action.classify()];
if (!handler) return;
handler.call(this, this.get('post'));
},
// Replies Button
renderReplies(post, buffer) {
if (!post.get('showRepliesBelow')) return;
const replyCount = post.get('reply_count');
buffer.push("");
},
renderButtons(post, buffer) {
const self = this;
const allButtons = [];
let visibleButtons = [];
if (typeof hiddenButtons === "undefined") {
if (!Em.isEmpty(this.siteSettings.post_menu_hidden_items)) {
hiddenButtons = this.siteSettings.post_menu_hidden_items.split('|');
} else {
hiddenButtons = [];
}
}
if (post.get("bookmarked")) {
hiddenButtons.removeObject("bookmark");
}
const yours = post.get('yours');
this.siteSettings.post_menu.split("|").forEach(function(i) {
const creator = self["buttonFor" + i.classify()];
if (creator) {
const button = creator.call(self, post);
if (button) {
allButtons.push(button);
if ((yours && button.opts.alwaysShowYours) ||
(post.get('wiki') && button.opts.alwaysShowWiki) ||
(hiddenButtons.indexOf(i) === -1)) {
visibleButtons.push(button);
}
}
}
});
// Only show ellipsis if there is more than one button hidden
// if there are no more buttons, we are not collapsed
const collapsed = this.get('collapsed');
if (!collapsed || (allButtons.length <= visibleButtons.length + 1)) {
visibleButtons = allButtons;
if (collapsed) { this.set('collapsed', false); }
} else {
visibleButtons.splice(visibleButtons.length - 1, 0, this.buttonForShowMoreActions(post));
}
const callbacks = PostMenuComponent._registerButtonCallbacks;
if (callbacks) {
_.each(callbacks, function(callback) {
callback.apply(self, [visibleButtons]);
});
}
buffer.push('
');
visibleButtons.forEach((b) => b.render(buffer));
buffer.push("
");
},
clickLikeCount() {
const likeAction = this.get('post.likeAction');
if (likeAction) {
const users = likeAction.get('users');
if (users && users.length) {
users.clear();
} else {
likeAction.loadUsers(this.get('post'));
}
}
},
clickReplies() {
if (this.get('post.replies.length') > 0) {
this.set('post.replies', []);
} else {
this.get('post').loadReplies();
}
},
// Delete button
buttonForDelete(post) {
let label, icon;
if (post.get('post_number') === 1) {
// If it's the first post, the delete/undo actions are related to the topic
const topic = post.get('topic');
if (topic.get('deleted_at')) {
if (!topic.get('details.can_recover')) { return; }
label = "topic.actions.recover";
icon = "undo";
} else {
if (!topic.get('details.can_delete')) { return; }
label = "topic.actions.delete";
icon = "trash-o";
}
} else {
// The delete actions target the post iteself
if (post.get('deleted_at') || post.get('user_deleted')) {
if (!post.get('can_recover')) { return; }
label = "post.controls.undelete";
icon = "undo";
} else {
if (!post.get('can_delete')) { return; }
label = "post.controls.delete";
icon = "trash-o";
}
}
const action = (icon === 'trash-o') ? 'delete' : 'recover';
let opts;
if (icon === "trash-o"){
opts = {className: 'delete'};
}
return new Button(action, label, icon, opts);
},
clickRecover(post) {
this.sendAction('recoverPost', post);
},
clickDelete(post) {
this.sendAction('deletePost', post);
},
// Like button
buttonForLike() {
const likeAction = this.get('post.likeAction');
if (!likeAction) { return; }
const className = likeAction.get('acted') ? 'has-like fade-out' : 'like';
const opts = {className: className};
if (likeAction.get('canToggle')) {
const descKey = likeAction.get('acted') ? 'post.controls.undo_like' : 'post.controls.like';
return new Button('like', descKey, 'heart', opts);
} else if (likeAction.get('acted')) {
opts.disabled = true;
return new Button('like', 'post.controls.has_liked', 'heart', opts);
}
},
buttonForLikeCount() {
const likeCount = this.get('post.likeAction.count') || 0;
if (likeCount > 0) {
const likedPost = !!this.get('post.likeAction.acted');
const label = likedPost ? 'post.has_likes_title_you' : 'post.has_likes_title';
return new Button('like-count', label, undefined, {
className: 'like-count highlight-action',
innerHTML: I18n.t("post.has_likes", { count: likeCount }),
labelOptions: {count: likedPost ? (likeCount-1) : likeCount}
});
}
},
clickLike(post) {
const $heart = this.$('.fa-heart'),
$likeButton = this.$('button[data-action=like]'),
acted = post.get('likeAction.acted'),
self = this;
if (acted) {
this.sendAction('toggleLike', post);
$likeButton.removeClass('has-like').addClass('like');
} else {
const scale = [1.0, 1.5];
animateHeart($heart, scale[0], scale[1], function() {
animateHeart($heart, scale[1], scale[0], function() {
self.sendAction('toggleLike', post);
$likeButton.removeClass('like').addClass('has-like');
});
});
}
},
// Flag button
buttonForFlag(post) {
if (Em.isEmpty(post.get('flagsAvailable'))) return;
return new Button('flag', 'post.controls.flag', 'flag');
},
clickFlag(post) {
this.sendAction('showFlags', post);
},
// Edit button
buttonForEdit(post) {
if (!post.get('can_edit')) return;
return new Button('edit', 'post.controls.edit', 'pencil', {
alwaysShowYours: true,
alwaysShowWiki: true
});
},
clickEdit(post) {
this.sendAction('editPost', post);
},
// Share button
buttonForShare(post) {
const options = {
shareUrl: post.get('shareUrl'),
postNumber: post.get('post_number')
};
return new Button('share', 'post.controls.share', 'link', options);
},
// Reply button
buttonForReply() {
if (!this.get('canCreatePost')) return;
const options = {className: 'create fade-out'};
if(!Discourse.Mobile.mobileView) {
options.textLabel = 'topic.reply.title';
}
return new Button('reply', 'post.controls.reply', 'reply', options);
},
clickReply(post) {
this.sendAction('replyToPost', post);
},
// Bookmark button
buttonForBookmark(post) {
if (!Discourse.User.current()) return;
let iconClass = 'read-icon',
buttonClass = 'bookmark',
tooltip = 'bookmarks.not_bookmarked';
if (post.get('bookmarked')) {
iconClass += ' bookmarked';
buttonClass += ' bookmarked';
tooltip = 'bookmarks.created';
}
return new Button('bookmark', tooltip, {className: buttonClass, innerHTML: ""});
},
clickBookmark(post) {
this.sendAction('toggleBookmark', post);
},
buttonForAdmin() {
if (!Discourse.User.currentProp('canManageTopic')) { return; }
return new Button('admin', 'post.controls.admin', 'wrench');
},
renderAdminPopup(post, buffer) {
if (!Discourse.User.currentProp('canManageTopic')) { return; }
const isWiki = post.get('wiki'),
wikiIcon = iconHTML('pencil-square-o'),
wikiText = isWiki ? I18n.t('post.controls.unwiki') : I18n.t('post.controls.wiki'),
isModerator = post.get('post_type') === this.site.get('post_types.moderator_action'),
postTypeIcon = iconHTML('shield'),
postTypeText = isModerator ? I18n.t('post.controls.revert_to_regular') : I18n.t('post.controls.convert_to_moderator'),
rebakePostIcon = iconHTML('cog'),
rebakePostText = I18n.t('post.controls.rebake'),
unhidePostIcon = iconHTML('eye'),
unhidePostText = I18n.t('post.controls.unhide');
const html = '';
buffer.push(html);
},
clickAdmin() {
const $postAdminMenu = this.$(".post-admin-menu");
$postAdminMenu.show();
$("html").on("mouseup.post-admin-menu", function() {
$postAdminMenu.hide();
$("html").off("mouseup.post-admin-menu");
});
},
clickToggleWiki() {
this.sendAction('toggleWiki', this.get('post'));
},
clickTogglePostType() {
this.sendAction("togglePostType", this.get("post"));
},
clickRebakePost() {
this.sendAction("rebakePost", this.get("post"));
},
clickUnhidePost() {
this.sendAction("unhidePost", this.get("post"));
},
buttonForShowMoreActions() {
return new Button('showMoreActions', 'show_more', 'ellipsis-h');
},
clickShowMoreActions() {
this.set('collapsed', false);
}
});
PostMenuComponent.reopenClass({
registerButton(callback){
this._registerButtonCallbacks = this._registerButtonCallbacks || [];
this._registerButtonCallbacks.push(callback);
}
});
export default PostMenuComponent;