import PropTypes from "prop-types"; import { PureComponent } from "react"; import { defineMessages, injectIntl } from "react-intl"; import classNames from "classnames"; import ImmutablePropTypes from "react-immutable-proptypes"; import { IconButton } from "flavours/glitch/components/icon_button"; import DropdownMenuContainer from "flavours/glitch/containers/dropdown_menu_container"; import { me } from "flavours/glitch/initial_state"; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from "flavours/glitch/permissions"; import { accountAdminLink, statusAdminLink } from "flavours/glitch/utils/backend_links"; const messages = defineMessages({ delete: { id: "status.delete", defaultMessage: "Delete" }, redraft: { id: "status.redraft", defaultMessage: "Delete & re-draft" }, edit: { id: "status.edit", defaultMessage: "Edit" }, direct: { id: "status.direct", defaultMessage: "Privately mention @{name}" }, mention: { id: "status.mention", defaultMessage: "Mention @{name}" }, reply: { id: "status.reply", defaultMessage: "Reply" }, reblog: { id: "status.reblog", defaultMessage: "Boost" }, reblog_private: { id: "status.reblog_private", defaultMessage: "Boost with original visibility" }, cancel_reblog_private: { id: "status.cancel_reblog_private", defaultMessage: "Unboost" }, cannot_reblog: { id: "status.cannot_reblog", defaultMessage: "This post cannot be boosted" }, favourite: { id: "status.favourite", defaultMessage: "Favorite" }, bookmark: { id: "status.bookmark", defaultMessage: "Bookmark" }, more: { id: "status.more", defaultMessage: "More" }, mute: { id: "status.mute", defaultMessage: "Mute @{name}" }, muteConversation: { id: "status.mute_conversation", defaultMessage: "Mute conversation" }, unmuteConversation: { id: "status.unmute_conversation", defaultMessage: "Unmute conversation" }, block: { id: "status.block", defaultMessage: "Block @{name}" }, report: { id: "status.report", defaultMessage: "Report @{name}" }, share: { id: "status.share", defaultMessage: "Share" }, pin: { id: "status.pin", defaultMessage: "Pin on profile" }, unpin: { id: "status.unpin", defaultMessage: "Unpin from profile" }, embed: { id: "status.embed", defaultMessage: "Embed" }, admin_account: { id: "status.admin_account", defaultMessage: "Open moderation interface for @{name}" }, admin_status: { id: "status.admin_status", defaultMessage: "Open this post in the moderation interface" }, admin_domain: { id: "status.admin_domain", defaultMessage: "Open moderation interface for {domain}" }, copy: { id: "status.copy", defaultMessage: "Copy link to post" }, openOriginalPage: { id: "account.open_original_page", defaultMessage: "Open original page" }, }); class ActionBar extends PureComponent { static contextTypes = { router: PropTypes.object, identity: PropTypes.object, }; static propTypes = { status: ImmutablePropTypes.map.isRequired, onReply: PropTypes.func.isRequired, onReblog: PropTypes.func.isRequired, onFavourite: PropTypes.func.isRequired, onBookmark: PropTypes.func.isRequired, onMute: PropTypes.func, onMuteConversation: PropTypes.func, onBlock: PropTypes.func, onDelete: PropTypes.func.isRequired, onEdit: PropTypes.func.isRequired, onDirect: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onReport: PropTypes.func, onPin: PropTypes.func, onEmbed: PropTypes.func, intl: PropTypes.object.isRequired, }; handleReplyClick = () => { this.props.onReply(this.props.status); }; handleReblogClick = (e) => { this.props.onReblog(this.props.status, e); }; handleFavouriteClick = (e) => { this.props.onFavourite(this.props.status, e); }; handleBookmarkClick = (e) => { this.props.onBookmark(this.props.status, e); }; handleDeleteClick = () => { this.props.onDelete(this.props.status, this.context.router.history); }; handleRedraftClick = () => { this.props.onDelete(this.props.status, this.context.router.history, true); }; handleEditClick = () => { this.props.onEdit(this.props.status, this.context.router.history); }; handleDirectClick = () => { this.props.onDirect(this.props.status.get("account"), this.context.router.history); }; handleMentionClick = () => { this.props.onMention(this.props.status.get("account"), this.context.router.history); }; handleMuteClick = () => { this.props.onMute(this.props.status.get("account")); }; handleConversationMuteClick = () => { this.props.onMuteConversation(this.props.status); }; handleBlockClick = () => { this.props.onBlock(this.props.status); }; handleReport = () => { this.props.onReport(this.props.status); }; handlePinClick = () => { this.props.onPin(this.props.status); }; handleShare = () => { navigator.share({ url: this.props.status.get("url"), }); }; handleEmbed = () => { this.props.onEmbed(this.props.status); }; handleCopy = () => { const url = this.props.status.get("url"); navigator.clipboard.writeText(url); }; render () { const { status, intl } = this.props; const { signedIn, permissions } = this.context.identity; const publicStatus = ["public", "unlisted"].includes(status.get("visibility")); const pinnableStatus = ["public", "unlisted", "private"].includes(status.get("visibility")); const mutingConversation = status.get("muted"); const writtenByMe = status.getIn(["account", "id"]) === me; const isRemote = status.getIn(["account", "username"]) !== status.getIn(["account", "acct"]); let menu = []; if (publicStatus && isRemote) { menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get("url") }); } menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy }); if (publicStatus && "share" in navigator) { menu.push({ text: intl.formatMessage(messages.share), action: this.handleShare }); } if (publicStatus && (signedIn || !isRemote)) { menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed }); } if (signedIn) { menu.push(null); if (writtenByMe) { if (pinnableStatus) { menu.push({ text: intl.formatMessage(status.get("pinned") ? messages.unpin : messages.pin), action: this.handlePinClick }); menu.push(null); } menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); menu.push(null); menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true }); menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(["account", "username"]) }), action: this.handleMentionClick }); menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(["account", "username"]) }), action: this.handleDirectClick }); menu.push(null); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(["account", "username"]) }), action: this.handleMuteClick, dangerous: true }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(["account", "username"]) }), action: this.handleBlockClick, dangerous: true }); menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(["account", "username"]) }), action: this.handleReport, dangerous: true }); if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { if (accountAdminLink !== undefined) { menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(["account", "username"]) }), href: accountAdminLink(status.getIn(["account", "id"])) }); } if (statusAdminLink !== undefined) { menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(["account", "id"]), status.get("id")) }); } } if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { const domain = status.getIn(["account", "acct"]).split("@")[1]; menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); } } } } const reblogPrivate = status.getIn(["account", "id"]) === me && status.get("visibility") === "private"; let reblogTitle; if (status.get("reblogged")) { reblogTitle = intl.formatMessage(messages.cancel_reblog_private); } else if (publicStatus) { reblogTitle = intl.formatMessage(messages.reblog); } else if (reblogPrivate) { reblogTitle = intl.formatMessage(messages.reblog_private); } else { reblogTitle = intl.formatMessage(messages.cannot_reblog); } return (