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 { connect } from "react-redux"; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from "mastodon/permissions"; import { IconButton } from "../../../components/icon_button"; import DropdownMenuContainer from "../../../containers/dropdown_menu_container"; import { me } from "../../../initial_state"; 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" }, blockDomain: { id: "account.block_domain", defaultMessage: "Block domain {domain}" }, unblockDomain: { id: "account.unblock_domain", defaultMessage: "Unblock domain {domain}" }, unmute: { id: "account.unmute", defaultMessage: "Unmute @{name}" }, unblock: { id: "account.unblock", defaultMessage: "Unblock @{name}" }, openOriginalPage: { id: "account.open_original_page", defaultMessage: "Open original page" }, }); const mapStateToProps = (state, { status }) => ({ relationship: state.getIn(["relationships", status.getIn(["account", "id"])]), }); class ActionBar extends PureComponent { static contextTypes = { router: PropTypes.object, identity: PropTypes.object, }; static propTypes = { status: ImmutablePropTypes.map.isRequired, relationship: ImmutablePropTypes.map, onReply: PropTypes.func.isRequired, onReblog: PropTypes.func.isRequired, onFavourite: PropTypes.func.isRequired, onBookmark: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, onEdit: PropTypes.func.isRequired, onDirect: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onMute: PropTypes.func, onUnmute: PropTypes.func, onBlock: PropTypes.func, onUnblock: PropTypes.func, onBlockDomain: PropTypes.func, onUnblockDomain: PropTypes.func, onMuteConversation: PropTypes.func, 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 = () => { this.props.onFavourite(this.props.status); }; 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 = () => { const { status, relationship, onMute, onUnmute } = this.props; const account = status.get("account"); if (relationship && relationship.get("muting")) { onUnmute(account); } else { onMute(account); } }; handleBlockClick = () => { const { status, relationship, onBlock, onUnblock } = this.props; const account = status.get("account"); if (relationship && relationship.get("blocking")) { onUnblock(account); } else { onBlock(status); } }; handleBlockDomain = () => { const { status, onBlockDomain } = this.props; const account = status.get("account"); onBlockDomain(account.get("acct").split("@")[1]); }; handleUnblockDomain = () => { const { status, onUnblockDomain } = this.props; const account = status.get("account"); onUnblockDomain(account.get("acct").split("@")[1]); }; handleConversationMuteClick = () => { this.props.onMuteConversation(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, relationship, 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 account = status.get("account"); 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(null); if (relationship && relationship.get("muting")) { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get("username") }), action: this.handleMuteClick }); } else { menu.push({ text: intl.formatMessage(messages.mute, { name: account.get("username") }), action: this.handleMuteClick, dangerous: true }); } if (relationship && relationship.get("blocking")) { menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get("username") }), action: this.handleBlockClick }); } else { menu.push({ text: intl.formatMessage(messages.block, { name: account.get("username") }), action: this.handleBlockClick, dangerous: true }); } menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(["account", "username"]) }), action: this.handleReport, dangerous: true }); if (account.get("acct") !== account.get("username")) { const domain = account.get("acct").split("@")[1]; menu.push(null); if (relationship && relationship.get("domain_blocking")) { menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain }); } else { menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain, dangerous: true }); } } if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(["account", "username"]) }), href: `/admin/accounts/${status.getIn(["account", "id"])}` }); menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(["account", "id"])}/statuses/${status.get("id")}` }); } if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { const domain = account.get("acct").split("@")[1]; menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); } } } } let replyIcon; if (status.get("in_reply_to_id", null) === null) { replyIcon = "reply"; } else { replyIcon = "reply-all"; } 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 (