import React from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' 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 { makeGetStatus } from '../../selectors' import { repost, unrepost, pin, unpin, isPin, bookmark, unbookmark, isBookmark, } from '../../actions/interactions'; import { muteAccount, unmuteAccount, } from '../../actions/accounts'; import { deleteStatus, editStatus, } from '../../actions/statuses'; import { quoteCompose } from '../../actions/compose' import { fetchBookmarkCollections, updateBookmarkCollectionStatus, } from '../../actions/bookmarks' import { fetchGroupRelationships, createRemovedAccount, groupRemoveStatus, pinGroupStatus, unpinGroupStatus, isPinnedGroupStatus, } from '../../actions/groups' import { initReport } from '../../actions/reports' import { openModal } from '../../actions/modal' import { closePopover, openPopover, } from '../../actions/popover' import { MODAL_PRO_UPGRADE, POPOVER_SHARE, } from '../../constants' import PopoverLayout from './popover_layout' import Button from '../button' import List from '../list' import Text from '../text' class StatusOptionsPopover extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, } state = { showingBookmarkCollections: false, } componentDidMount() { const { status, statusId, groupRelationships, } = this.props if (status.get('pinnable')) { this.props.fetchIsPin(statusId) } this.props.fetchIsBookmark(statusId) if (!!status.get('group')) { this.props.fetchIsPinnedGroupStatus(status.getIn(['group', 'id'], null), statusId) } if (!this.props.groupRelationships && this.props.groupId) { this.props.onFetchGroupRelationships(this.props.groupId) } } 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) } handleGroupPinStatus = () => { this.props.onPinGroupStatus(this.props.status) } handleBookmarkClick = () => { // : todo : add to specific bookmark collection if (this.props.isPro) { this.props.onBookmark(this.props.status) } else { this.props.onOpenProUpgradeModal() } } handleBookmarkChangeClick = () => { if (!this.props.bookmarkCollectionsIsFetched) this.props.onFetchBookmarkCollections() this.setState({ showingBookmarkCollections: true }) } handleBookmarkChangeBackClick = () => { this.setState({ showingBookmarkCollections: false }) } handleBookmarkChangeSelectClick = (bookmarkCollectionId) => { this.props.onUpdateBookmarkCollectionStatus(this.props.status.get('id'), bookmarkCollectionId) } 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) } handleOnOpenSharePopover = () => { this.props.onOpenSharePopover(this.props.innerRef, this.props.status) } handleClosePopover = () => { this.props.onClosePopover() } render() { const { isXS, intl, status, groupRelationships, bookmarkCollections, } = this.props const { showingBookmarkCollections } = this.state if (!status) return
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') || groupRelationships.get('moderator')) : false let menu = [] if (me) { if (isReply) { menu.push({ icon: 'pencil', hideArrow: true, title: intl.formatMessage(messages.repostWithComment), onClick: this.handleQuoteClick, }) } menu.push({ icon: 'bookmark', hideArrow: status.get('bookmarked'), title: intl.formatMessage(status.get('bookmarked') ? messages.unbookmark : messages.bookmark), onClick: this.handleBookmarkClick, }) if (status.get('bookmarked')) { // : todo : // menu.push({ // icon: 'bookmark', // title: 'Update bookmark collection', // onClick: this.handleBookmarkChangeClick, // }) } 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, }) } } if (withGroupAdmin || (!!groupRelationships && isStaff)) { 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, }) menu.push(null) menu.push({ icon: 'pin', hideArrow: true, title: intl.formatMessage(status.get('pinned_by_group') ? messages.groupUnpin : messages.groupPin), onClick: this.handleGroupPinStatus, }) } menu.push(null) menu.push({ icon: 'share', hideArrow: true, title: intl.formatMessage(messages.share), onClick: this.handleOnOpenSharePopover, }) 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'])}`, openInNewTab: true, }) menu.push({ title: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/account_statuses/${status.get('id')}`, openInNewTab: true, }) } const popoverWidth = !isStaff ? 260 : 362 let bookmarkCollectionItems = !!bookmarkCollections ? bookmarkCollections.map((bookmarkCollection) => ({ hideArrow: true, onClick: () => this.handleBookmarkChangeSelectClick(bookmarkCollection.get('id')), title: bookmarkCollection.get('title'), isActive: bookmarkCollection.get('id') === status.get('bookmark_collection_id'), })) : [] bookmarkCollectionItems = bookmarkCollectionItems.unshift({ hideArrow: true, onClick: () => this.handleBookmarkChangeSelectClick('saved'), title: 'Saved', isActive: !status.get('bookmark_collection_id'), }) return ( { !showingBookmarkCollections && } { showingBookmarkCollections &&
}
) } } 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}' }, pin: { id: 'status.pin', defaultMessage: 'Pin on profile' }, unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' }, groupPin: { id: 'status.group_pin', defaultMessage: 'Pin in group' }, groupUnpin: { id: 'status.group_unpin', defaultMessage: 'Unpin from group' }, 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' }, share: { id: 'status.share_gab', defaultMessage: 'Share gab' }, }) const mapStateToProps = (state, { statusId }) => { if (!me) return null const status = statusId ? makeGetStatus()(state, { id: statusId }) : undefined const groupId = status ? status.getIn(['group', 'id']) : undefined const groupRelationships = state.getIn(['group_relationships', groupId]) return { status, groupId, groupRelationships, isPro: state.getIn(['accounts', me, 'is_pro']), bookmarkCollectionsIsFetched: state.getIn(['bookmark_collections', 'isFetched']), bookmarkCollections: state.getIn(['bookmark_collections', 'items']), } } const mapDispatchToProps = (dispatch) => ({ fetchIsPin(statusId) { dispatch(isPin(statusId)) }, fetchIsBookmark(statusId) { dispatch(isBookmark(statusId)) }, fetchIsPinnedGroupStatus(groupId, statusId) { dispatch(isPinnedGroupStatus(groupId, statusId)) }, 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)) }, onBlock(status) { dispatch(closePopover()) const account = status.get('account') dispatch(openModal('BLOCK_ACCOUNT', { accountId: account.get('id'), })) }, onMute(account) { dispatch(closePopover()) if (account.getIn(['relationship', 'muting'])) { dispatch(unmuteAccount(account.get('id'))); } else { dispatch(openModal('MUTE', { 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])) }, onOpenSharePopover(targetRef, status) { dispatch(closePopover()) dispatch(openPopover(POPOVER_SHARE, { targetRef, status, position: 'top', })) }, onOpenProUpgradeModal() { dispatch(closePopover()) dispatch(openModal(MODAL_PRO_UPGRADE)) }, onPinGroupStatus(status) { dispatch(closePopover()) if (status.get('pinned_by_group')) { dispatch(unpinGroupStatus(status.getIn(['group', 'id']), status.get('id'))) } else { dispatch(pinGroupStatus(status.getIn(['group', 'id']), status.get('id'))) } }, onFetchBookmarkCollections() { dispatch(fetchBookmarkCollections()) }, onUpdateBookmarkCollectionStatus(statusId, bookmarkCollectionId) { dispatch(updateBookmarkCollectionStatus(statusId, bookmarkCollectionId)) dispatch(closePopover()) }, onClosePopover: () => dispatch(closePopover()), }) StatusOptionsPopover.propTypes = { status: ImmutablePropTypes.map, statusId: PropTypes.string.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, onPin: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, onFetchGroupRelationships: PropTypes.func.isRequired, onOpenProUpgradeModal: PropTypes.func.isRequired, onClosePopover: PropTypes.func.isRequired, fetchIsPinnedGroupStatus: PropTypes.func.isRequired, fetchIsBookmark: PropTypes.func.isRequired, fetchIsPin: PropTypes.func.isRequired, onFetchBookmarkCollections: PropTypes.func.isRequired, onUpdateBookmarkCollectionStatus: PropTypes.func.isRequired, isXS: PropTypes.bool, isPro: PropTypes.bool, } export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(StatusOptionsPopover))