const post = this.postView.post;
return (
<Link
- className={`d-inline-block ${
+ className={`d-inline ${
!post.featured_community && !post.featured_local
? "text-body"
: "text-primary"
to={`/post/${post.id}`}
title={i18n.t("comments")}
>
- <div
- className="d-inline-block"
+ <span
+ className="d-inline"
dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
/>
</Link>
return (
<div className="post-title overflow-hidden">
- <h5>
- {url ? (
- this.props.showBody ? (
- <a
- className={`d-inline-block ${
- !post.featured_community && !post.featured_local
- ? "text-body"
- : "text-primary"
- }`}
- href={url}
- title={url}
- rel={relTags}
- >
- <div
- className="d-inline-block"
- dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
- />
- </a>
- ) : (
- this.postLink
- )
+ <h5 className="d-inline">
+ {url && this.props.showBody ? (
+ <a
+ className={
+ !post.featured_community && !post.featured_local
+ ? "text-body"
+ : "text-primary"
+ }
+ href={url}
+ title={url}
+ rel={relTags}
+ dangerouslySetInnerHTML={mdToHtmlInline(post.name)}
+ ></a>
) : (
this.postLink
)}
- {(url && isImage(url)) ||
- (post.thumbnail_url && (
- <button
- className="btn btn-link text-monospace text-muted small d-inline-block"
- data-tippy-content={i18n.t("expand_here")}
- onClick={linkEvent(this, this.handleImageExpandClick)}
- >
- <Icon
- icon={
- !this.state.imageExpanded ? "plus-square" : "minus-square"
- }
- classes="icon-inline"
- />
- </button>
- ))}
- {post.removed && (
- <small className="ml-2 text-muted font-italic">
- {i18n.t("removed")}
- </small>
- )}
- {post.deleted && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("deleted")}
- >
- <Icon icon="trash" classes="icon-inline text-danger" />
- </small>
- )}
- {post.locked && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("locked")}
- >
- <Icon icon="lock" classes="icon-inline text-danger" />
- </small>
- )}
- {post.featured_community && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("featured")}
- >
- <Icon icon="pin" classes="icon-inline text-primary" />
- </small>
- )}
- {post.featured_local && (
- <small
- className="unselectable pointer ml-2 text-muted font-italic"
- data-tippy-content={i18n.t("featured")}
- >
- <Icon icon="pin" classes="icon-inline text-secondary" />
- </small>
- )}
- {post.nsfw && (
- <small className="ml-2 text-muted font-italic">
- {i18n.t("nsfw")}
- </small>
- )}
</h5>
+ {(url && isImage(url)) ||
+ (post.thumbnail_url && (
+ <button
+ className="btn btn-link text-monospace text-muted small d-inline-block"
+ data-tippy-content={i18n.t("expand_here")}
+ onClick={linkEvent(this, this.handleImageExpandClick)}
+ >
+ <Icon
+ icon={
+ !this.state.imageExpanded ? "plus-square" : "minus-square"
+ }
+ classes="icon-inline"
+ />
+ </button>
+ ))}
+ {post.removed && (
+ <small className="ml-2 text-muted font-italic">
+ {i18n.t("removed")}
+ </small>
+ )}
+ {post.deleted && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("deleted")}
+ >
+ <Icon icon="trash" classes="icon-inline text-danger" />
+ </small>
+ )}
+ {post.locked && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("locked")}
+ >
+ <Icon icon="lock" classes="icon-inline text-danger" />
+ </small>
+ )}
+ {post.featured_community && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("featured")}
+ >
+ <Icon icon="pin" classes="icon-inline text-primary" />
+ </small>
+ )}
+ {post.featured_local && (
+ <small
+ className="unselectable pointer ml-2 text-muted font-italic"
+ data-tippy-content={i18n.t("featured")}
+ >
+ <Icon icon="pin" classes="icon-inline text-secondary" />
+ </small>
+ )}
+ {post.nsfw && (
+ <small className="ml-2 text-muted font-italic">
+ {i18n.t("nsfw")}
+ </small>
+ )}
</div>
);
}
{mobile && !this.props.viewOnly && this.mobileVotes}
{UserService.Instance.myUserInfo &&
!this.props.viewOnly &&
- this.postActions(mobile)}
+ this.postActions()}
</div>
);
}
- postActions(mobile = false) {
+ get hasAdvancedButtons() {
+ return (
+ this.myPost ||
+ (this.showBody && this.postView.post.body) ||
+ amMod(this.props.moderators) ||
+ amAdmin() ||
+ this.canMod_ ||
+ this.canAdmin_
+ );
+ }
+
+ postActions() {
// Possible enhancement: Priority+ pattern instead of just hard coding which get hidden behind the show more button.
// Possible enhancement: Make each button a component.
const post_view = this.postView;
<>
{this.saveButton}
{this.crossPostButton}
- {mobile && this.showMoreButton}
- {(!mobile || this.state.showAdvanced) && (
- <>
- {!this.myPost && (
- <>
- {this.reportButton}
- {this.blockButton}
- </>
- )}
- {this.myPost && (this.showBody || this.state.showAdvanced) && (
- <>
- {this.editButton}
- {this.deleteButton}
- </>
- )}
- </>
- )}
- {this.state.showAdvanced && (
- <>
- {this.showBody && post_view.post.body && this.viewSourceButton}
- {/* Any mod can do these, not limited to hierarchy*/}
- {(amMod(this.props.moderators) || amAdmin()) && (
- <>
- {this.lockButton}
- {this.featureButton}
- </>
- )}
- {(this.canMod_ || this.canAdmin_) && <>{this.modRemoveButton}</>}
- </>
+
+ {this.showBody && post_view.post.body && this.viewSourceButton}
+
+ {this.hasAdvancedButtons && (
+ <div className="dropdown">
+ <button
+ className="btn btn-link btn-animate text-muted py-0 dropdown-toggle"
+ onClick={linkEvent(this, this.handleShowAdvanced)}
+ data-tippy-content={i18n.t("more")}
+ data-bs-toggle="dropdown"
+ aria-expanded="false"
+ aria-controls="advancedButtonsDropdown"
+ aria-label={i18n.t("more")}
+ >
+ <Icon icon="more-vertical" inline />
+ </button>
+
+ <ul className="dropdown-menu" id="advancedButtonsDropdown">
+ {!this.myPost ? (
+ <>
+ <li>{this.reportButton}</li>
+ <li>{this.blockButton}</li>
+ </>
+ ) : (
+ <>
+ <li>{this.editButton}</li>
+ <li>{this.deleteButton}</li>
+ </>
+ )}
+
+ {/* Any mod can do these, not limited to hierarchy*/}
+ {(amMod(this.props.moderators) || amAdmin()) && (
+ <>
+ <li>
+ <hr className="dropdown-divider" />
+ </li>
+ <li>{this.lockButton}</li>
+ {this.featureButtons}
+ </>
+ )}
+
+ {(this.canMod_ || this.canAdmin_) && (
+ <li>{this.modRemoveButton}</li>
+ )}
+ </ul>
+ </div>
)}
- {!mobile && this.showMoreButton}
</>
);
}
get reportButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleShowReportDialog)}
data-tippy-content={i18n.t("show_report_dialog")}
aria-label={i18n.t("show_report_dialog")}
get blockButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleBlockPersonClick)}
data-tippy-content={i18n.t("block_user")}
aria-label={i18n.t("block_user")}
get editButton() {
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleEditClick)}
- data-tippy-content={i18n.t("edit")}
aria-label={i18n.t("edit")}
>
- <Icon icon="edit" inline />
+ <Icon classes="mr-1" icon="edit" inline />
+ {i18n.t("edit")}
</button>
);
}
const label = !deleted ? i18n.t("delete") : i18n.t("restore");
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleDeleteClick)}
- data-tippy-content={label}
aria-label={label}
>
{this.state.deleteLoading ? (
<Spinner />
) : (
- <Icon
- icon="trash"
- classes={classNames({ "text-danger": deleted })}
- inline
- />
+ <>
+ <Icon
+ icon="trash"
+ classes={classNames("mr-1", { "text-danger": deleted })}
+ inline
+ />
+ {label}
+ </>
)}
</button>
);
}
- get showMoreButton() {
- return (
- <button
- className="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleShowAdvanced)}
- data-tippy-content={i18n.t("more")}
- aria-label={i18n.t("more")}
- >
- <Icon icon="more-vertical" inline />
- </button>
- );
- }
-
get viewSourceButton() {
return (
<button
const label = locked ? i18n.t("unlock") : i18n.t("lock");
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(this, this.handleModLock)}
- data-tippy-content={label}
aria-label={label}
>
{this.state.lockLoading ? (
<Spinner />
) : (
- <Icon
- icon="lock"
- classes={classNames({ "text-danger": locked })}
- inline
- />
+ <>
+ <Icon
+ icon="lock"
+ classes={classNames("mr-1", { "text-danger": locked })}
+ inline
+ />
+ {label}
+ </>
)}
</button>
);
}
- get featureButton() {
+ get featureButtons() {
const featuredCommunity = this.postView.post.featured_community;
const labelCommunity = featuredCommunity
? i18n.t("unfeature_from_community")
? i18n.t("unfeature_from_local")
: i18n.t("feature_in_local");
return (
- <span>
- <button
- className="btn btn-link btn-animate text-muted py-0 pl-0"
- onClick={linkEvent(this, this.handleModFeaturePostCommunity)}
- data-tippy-content={labelCommunity}
- aria-label={labelCommunity}
- >
- {this.state.featureCommunityLoading ? (
- <Spinner />
- ) : (
- <span>
- <Icon
- icon="pin"
- classes={classNames({ "text-success": featuredCommunity })}
- inline
- />
- {i18n.t("community")}
- </span>
- )}
- </button>
- {amAdmin() && (
+ <>
+ <li>
<button
- className="btn btn-link btn-animate text-muted py-0"
- onClick={linkEvent(this, this.handleModFeaturePostLocal)}
- data-tippy-content={labelLocal}
- aria-label={labelLocal}
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
+ onClick={linkEvent(this, this.handleModFeaturePostCommunity)}
+ data-tippy-content={labelCommunity}
+ aria-label={labelCommunity}
>
- {this.state.featureLocalLoading ? (
+ {this.state.featureCommunityLoading ? (
<Spinner />
) : (
- <span>
+ <>
<Icon
icon="pin"
- classes={classNames({ "text-success": featuredLocal })}
+ classes={classNames("mr-1", {
+ "text-success": featuredCommunity,
+ })}
inline
/>
- {i18n.t("local")}
- </span>
+ {i18n.t("community")}
+ </>
)}
</button>
- )}
- </span>
+ </li>
+ <li>
+ {amAdmin() && (
+ <button
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
+ onClick={linkEvent(this, this.handleModFeaturePostLocal)}
+ data-tippy-content={labelLocal}
+ aria-label={labelLocal}
+ >
+ {this.state.featureLocalLoading ? (
+ <Spinner />
+ ) : (
+ <>
+ <Icon
+ icon="pin"
+ classes={classNames("mr-1", {
+ "text-success": featuredLocal,
+ })}
+ inline
+ />
+ {i18n.t("local")}
+ </>
+ )}
+ </button>
+ )}
+ </li>
+ </>
);
}
const removed = this.postView.post.removed;
return (
<button
- className="btn btn-link btn-animate text-muted py-0"
+ className="btn btn-link btn-sm d-flex align-items-center rounded-0 dropdown-item"
onClick={linkEvent(
this,
!removed ? this.handleModRemoveShow : this.handleModRemoveSubmit