import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePureComponent from 'react-immutable-pure-component' import { FormattedMessage, defineMessages, injectIntl } from 'react-intl' import { me, isStaff, boostModal, deleteModal } from '../../initial_state' import { repost, unrepost, pin, unpin, bookmark, unbookmark, } from '../../actions/interactions'; import { muteStatus, unmuteStatus, deleteStatus, editStatus, } from '../../actions/statuses'; import { quoteCompose } from '../../actions/compose' import { fetchGroupRelationships, createRemovedAccount, groupRemoveStatus, } from '../../actions/groups' import { initMuteModal } from '../../actions/mutes' import { initReport } from '../../actions/reports' import { openModal } from '../../actions/modal' import { closePopover } from '../../actions/popover' import { MODAL_EMBED, MODAL_PRO_UPGRADE, } from '../../constants' import PopoverLayout from './popover_layout' import List from '../list' const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, edit: { id: 'status.edit', defaultMessage: 'Edit' }, mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' }, reply: { id: 'status.reply', defaultMessage: 'Reply' }, more: { id: 'status.more', defaultMessage: 'More' }, share: { id: 'status.share', defaultMessage: 'Share' }, replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' }, repost: { id: 'repost', defaultMessage: 'Repost' }, quote: { id: 'status.quote', defaultMessage: 'Quote' }, repost_private: { id: 'status.repost', defaultMessage: 'Repost' }, cancel_repost_private: { id: 'status.cancel_repost_private', defaultMessage: 'Remove Repost' }, cannot_repost: { id: 'status.cannot_repost', defaultMessage: 'This post cannot be reposted' }, cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' }, like: { id: 'status.like', defaultMessage: 'Like' }, report: { id: 'status.report', defaultMessage: 'Report @{name}' }, muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' }, unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' }, pin: { id: 'status.pin', defaultMessage: 'Pin on profile' }, unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' }, bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark status' }, unbookmark: { id: 'status.unbookmark', defaultMessage: 'Remove bookmark' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' }, group_remove_account: { id: 'status.remove_account_from_group', defaultMessage: 'Remove account from group' }, group_remove_post: { id: 'status.remove_post_from_group', defaultMessage: 'Remove status from group' }, repostWithComment: { id: 'repost_with_comment', defaultMessage: 'Repost with comment' }, embed: { id: 'status.embed', defaultMessage: 'Embed' }, email: { id: 'status.email', defaultMessage: 'Email this gab' }, copy: { id: 'status.copy', defaultMessage: 'Copy link to status' }, }) const mapStateToProps = (state, { status }) => { if (!me) return null const groupId = status ? status.getIn(['group', 'id']) : undefined const groupRelationships = state.getIn(['group_relationships', groupId]) return { groupId, groupRelationships, isPro: state.getIn(['accounts', me, 'is_pro']), } } const mapDispatchToProps = (dispatch) => ({ onMuteConversation(status) { dispatch(closePopover()) if (status.get('muted')) { dispatch(unmuteStatus(status.get('id'))) } else { dispatch(muteStatus(status.get('id'))) } }, onPin(status) { dispatch(closePopover()) if (status.get('pinned')) { dispatch(unpin(status)) } else { dispatch(pin(status)) } }, onBookmark(status) { dispatch(closePopover()) if (status.get('bookmarked')) { dispatch(unbookmark(status)) } else { dispatch(bookmark(status)) } }, onQuote(status, router) { dispatch(closePopover()) dispatch((_, getState) => { const state = getState() if (state.getIn(['compose', 'text']).trim().length !== 0) { dispatch(openModal(MODAL_CONFIRM, { message: intl.formatMessage(messages.quoteMessage), confirm: intl.formatMessage(messages.quoteConfirm), onConfirm: () => dispatch(quoteCompose(status, router)), })) } else { dispatch(quoteCompose(status, router)) } }) }, onRepost(status) { dispatch(closePopover()) const alreadyReposted = status.get('reblogged') if (boostModal && !alreadyReposted) { dispatch(openModal(MODAL_BOOST, { status, onRepost: () => dispatch(repost(status)), })) } else { if (alreadyReposted) { dispatch(unrepost(status)) } else { dispatch(repost(status)) } } }, onDelete(status, history) { dispatch(closePopover()) if (!deleteModal) { dispatch(deleteStatus(status.get('id'), history)) } else { dispatch(openModal('CONFIRM', { message: , confirm: , onConfirm: () => dispatch(deleteStatus(status.get('id'), history)), })) } }, onEdit(status) { dispatch(closePopover()) dispatch(editStatus(status)) }, onMute(account) { dispatch(closePopover()) dispatch(initMuteModal(account)) }, onBlock(status) { dispatch(closePopover()) const account = status.get('account') dispatch(openModal('BLOCK_ACCOUNT', { accountId: account.get('id'), })) }, onReport(status) { dispatch(closePopover()) dispatch(initReport(status.get('account'), status)) }, onGroupRemoveAccount(groupId, accountId) { dispatch(closePopover()) dispatch(createRemovedAccount(groupId, accountId)) }, onGroupRemoveStatus(groupId, statusId) { dispatch(closePopover()) dispatch(groupRemoveStatus(groupId, statusId)) }, onFetchGroupRelationships(groupId) { dispatch(fetchGroupRelationships([groupId])) }, onOpenEmbedModal(url) { dispatch(closePopover()) dispatch(openModal(MODAL_EMBED, { url, })) }, onOpenProUpgradeModal() { dispatch(closePopover()) dispatch(openModal(MODAL_PRO_UPGRADE)) }, onClosePopover: () => dispatch(closePopover()), }) export default @injectIntl @connect(mapStateToProps, mapDispatchToProps) class StatusOptionsPopover extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, } static propTypes = { status: ImmutablePropTypes.map.isRequired, groupRelationships: ImmutablePropTypes.map, groupId: PropTypes.string, onQuote: PropTypes.func.isRequired, onRepost: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired, onMuteConversation: PropTypes.func.isRequired, onPin: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, onFetchGroupRelationships: PropTypes.func.isRequired, onOpenEmbedModal: PropTypes.func.isRequired, onOpenProUpgradeModal: PropTypes.func.isRequired, onClosePopover: PropTypes.func.isRequired, isXS: PropTypes.bool, isPro: PropTypes.bool, } updateOnProps = [ 'status', 'groupRelationships', 'isXS', ] componentDidMount() { if (!this.props.groupRelationships && this.props.groupId) { this.props.onFetchGroupRelationships(this.props.groupId) } } handleConversationMuteClick = () => { this.props.onMuteConversation(this.props.status) } handleGroupRemoveAccount = () => { const { status } = this.props this.props.onGroupRemoveAccount(status.getIn(['group', 'id']), status.getIn(['account', 'id'])) } handleGroupRemovePost = () => { const { status } = this.props this.props.onGroupRemoveStatus(status.getIn(['group', 'id']), status.get('id')) } handleReport = () => { this.props.onReport(this.props.status) } handleBlockClick = () => { this.props.onBlock(this.props.status) } handleMuteClick = () => { this.props.onMute(this.props.status.get('account')) } handlePinClick = () => { this.props.onPin(this.props.status) } handleBookmarkClick = () => { if (this.props.isPro) { this.props.onBookmark(this.props.status) } else { this.props.onOpenProUpgradeModal() } } handleDeleteClick = () => { this.props.onDelete(this.props.status) } handleEditClick = () => { this.props.onEdit(this.props.status) } handleRepostClick = (e) => { this.props.onRepost(this.props.status, e) } handleQuoteClick = (e) => { this.props.onQuote(this.props.status, this.context.router) } handleOnOpenEmbedModal = () => { this.props.onOpenEmbedModal(this.props.status.get('url')) } handleCopy = () => { const url = this.props.status.get('url'); const textarea = document.createElement('textarea'); textarea.textContent = url; textarea.style.position = 'fixed'; document.body.appendChild(textarea); try { textarea.select(); document.execCommand('copy'); } catch (e) { // } document.body.removeChild(textarea); this.handleClosePopover() } handleClosePopover = () => { this.props.onClosePopover() } render() { const { status, intl, groupRelationships, isXS, } = this.props const mutingConversation = status.get('muted') const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')) const isReply = !!status.get('in_reply_to_id') const withGroupAdmin = !!groupRelationships ? groupRelationships.get('admin') : false const mailToHref = !status ? undefined : `mailto:?subject=Gab&body=${status.get('url')}` let menu = [] if (me) { if (status.getIn(['account', 'id']) === me) { menu.push({ icon: 'audio-mute', hideArrow: true, title: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), onClick: this.handleConversationMuteClick, }) } if (isReply) { menu.push({ icon: 'repost', hideArrow: true, title: intl.formatMessage(status.get('reblogged') ? messages.cancel_repost_private : messages.repost_private), onClick: this.handleRepostClick, }) menu.push({ icon: 'pencil', hideArrow: true, title: intl.formatMessage(messages.repostWithComment), onClick: this.handleQuoteClick, }) } menu.push({ icon: 'bookmark', hideArrow: true, title: intl.formatMessage(status.get('bookmarked') ? messages.unbookmark : messages.bookmark), onClick: this.handleBookmarkClick, }) if (status.getIn(['account', 'id']) === me) { if (publicStatus) { menu.push({ icon: 'pin', hideArrow: true, title: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), onClick: this.handlePinClick, }) } menu.push({ icon: 'trash', hideArrow: true, title: intl.formatMessage(messages.delete), onClick: this.handleDeleteClick, }) menu.push({ icon: 'pencil', hideArrow: true, title: intl.formatMessage(messages.edit), onClick: this.handleEditClick, }) } else { menu.push({ icon: 'audio-mute', hideArrow: true, title: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), onClick: this.handleMuteClick, }) menu.push({ icon: 'block', hideArrow: true, title: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), onClick: this.handleBlockClick, }) menu.push({ icon: 'warning', hideArrow: true, title: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), onClick: this.handleReport, }) } } menu.push(null) menu.push({ icon: 'copy', hideArrow: true, title: intl.formatMessage(messages.copy), onClick: this.handleCopy, }) menu.push({ icon: 'email', hideArrow: true, title: intl.formatMessage(messages.email), href: mailToHref, }) menu.push({ icon: 'code', hideArrow: true, title: intl.formatMessage(messages.embed), onClick: this.handleOnOpenEmbedModal, }) if (withGroupAdmin) { menu.push(null) menu.push({ icon: 'trash', hideArrow: true, title: intl.formatMessage(messages.group_remove_account), onClick: this.handleGroupRemoveAccount, }) menu.push({ icon: 'trash', hideArrow: true, title: intl.formatMessage(messages.group_remove_post), onClick: this.handleGroupRemovePost, }) } if (isStaff) { menu.push(null) menu.push({ title: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }) menu.push({ title: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }) } return ( ) } }