From 0364a4000bd17f00794dba4aef86562390601513 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Wed, 27 May 2020 01:13:46 -0400 Subject: [PATCH] Added quote posting to status bar, Removed share from status bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added: - quote icon - quote posting to status bar • Removed: - share from status bar (all share items now in status ellipsis menu) - repost options popover - share option popover - unused code, constants --- app/javascript/gabsocial/assets/quote_icon.js | 24 ++++ app/javascript/gabsocial/components/icon.js | 2 + .../popover/repost_options_popover.js | 125 ------------------ .../popover/status_options_popover.js | 61 ++++++++- .../popover/status_share_popover.js | 101 -------------- app/javascript/gabsocial/components/status.js | 6 + .../gabsocial/components/status_action_bar.js | 37 ++---- app/javascript/gabsocial/constants.js | 2 - .../gabsocial/containers/status_container.js | 83 +++++++----- .../features/ui/util/async_components.js | 2 - 10 files changed, 155 insertions(+), 288 deletions(-) create mode 100644 app/javascript/gabsocial/assets/quote_icon.js delete mode 100644 app/javascript/gabsocial/components/popover/repost_options_popover.js delete mode 100644 app/javascript/gabsocial/components/popover/status_share_popover.js diff --git a/app/javascript/gabsocial/assets/quote_icon.js b/app/javascript/gabsocial/assets/quote_icon.js new file mode 100644 index 00000000..35c891e7 --- /dev/null +++ b/app/javascript/gabsocial/assets/quote_icon.js @@ -0,0 +1,24 @@ +const QuoteIcon = ({ + className = '', + size = '16px', + title = 'Quote', +}) => ( + + + + + +) + +export default QuoteIcon \ No newline at end of file diff --git a/app/javascript/gabsocial/components/icon.js b/app/javascript/gabsocial/components/icon.js index 089311bd..fd10056e 100644 --- a/app/javascript/gabsocial/components/icon.js +++ b/app/javascript/gabsocial/components/icon.js @@ -54,6 +54,7 @@ import PinIcon from '../assets/pin_icon' import PlayIcon from '../assets/play_icon' import PollIcon from '../assets/poll_icon' import ProIcon from '../assets/pro_icon' +import QuoteIcon from '../assets/quote_icon' import RepostIcon from '../assets/repost_icon' import RichTextIcon from '../assets/rich_text_icon' import SearchIcon from '../assets/search_icon' @@ -130,6 +131,7 @@ const ICONS = { 'play': PlayIcon, 'poll': PollIcon, 'pro': ProIcon, + 'quote': QuoteIcon, 'repost': RepostIcon, 'rich-text': RichTextIcon, 'search': SearchIcon, diff --git a/app/javascript/gabsocial/components/popover/repost_options_popover.js b/app/javascript/gabsocial/components/popover/repost_options_popover.js deleted file mode 100644 index 65cafa1d..00000000 --- a/app/javascript/gabsocial/components/popover/repost_options_popover.js +++ /dev/null @@ -1,125 +0,0 @@ -import ImmutablePureComponent from 'react-immutable-pure-component' -import ImmutablePropTypes from 'react-immutable-proptypes' -import { defineMessages, injectIntl } from 'react-intl' -import { - MODAL_BOOST, - MODAL_CONFIRM, - MODAL_UNAUTHORIZED, -} from '../../constants' -import { boostModal, me } from '../../initial_state' -import { quoteCompose } from '../../actions/compose' -import { repost, unrepost } from '../../actions/interactions' -import { closePopover } from '../../actions/popover' -import { openModal } from '../../actions/modal' -import PopoverLayout from './popover_layout' -import List from '../list' - -const messages = defineMessages({ - repost: { id: 'repost', defaultMessage: 'Repost' }, - removeRepost: { id: 'status.cancel_repost_private', defaultMessage: 'Remove Repost' }, - repostWithComment: { id: 'repost_with_comment', defaultMessage: 'Repost with comment' }, - quoteMessage: { id: 'confirmations.quote.message', defaultMessage: 'Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, - quoteConfirm: { id: 'confirmations.quote.confirm', defaultMessage: 'Quote' }, -}) - -const mapDispatchToProps = (dispatch, { intl }) => ({ - onQuote (status, router) { - if (!me) return dispatch(openModal(MODAL_UNAUTHORIZED)) - - 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) { - if (!me) return dispatch(openModal(MODAL_UNAUTHORIZED)) - - 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)) - } - } - }, -}) - -export default -@injectIntl -@connect(null, mapDispatchToProps) -class RepostOptionsPopover extends ImmutablePureComponent { - - static contextTypes = { - router: PropTypes.object, - } - - static defaultProps = { - intl: PropTypes.object.isRequired, - onQuote: PropTypes.func.isRequired, - onRepost: PropTypes.func.isRequired, - status: ImmutablePropTypes.map.isRequired, - isXS: PropTypes.bool, - } - - updateOnProps = [ - 'status', - ] - - handleOnQuote = () => { - this.props.onQuote(this.props.status, this.context.router) - } - - handleOnRepost = () => { - this.props.onRepost(this.props.status) - } - - render() { - const { intl, status, isXS } = this.props - - const alreadyReposted = status.get('reblogged') - - return ( - - - - ) - } - -} \ No newline at end of file diff --git a/app/javascript/gabsocial/components/popover/status_options_popover.js b/app/javascript/gabsocial/components/popover/status_options_popover.js index ca7897df..fbc3a7e8 100644 --- a/app/javascript/gabsocial/components/popover/status_options_popover.js +++ b/app/javascript/gabsocial/components/popover/status_options_popover.js @@ -24,6 +24,7 @@ import { initMuteModal } from '../../actions/mutes' import { initReport } from '../../actions/reports' import { openModal } from '../../actions/modal' import { closePopover } from '../../actions/popover' +import { MODAL_EMBED } from '../../constants' import PopoverLayout from './popover_layout' import List from '../list' @@ -53,6 +54,9 @@ const messages = defineMessages({ 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 }) => { @@ -173,7 +177,16 @@ const mapDispatchToProps = (dispatch) => ({ onFetchGroupRelationships(groupId) { dispatch(fetchGroupRelationships([groupId])) - } + }, + + onOpenEmbedModal(url) { + dispatch(closePopover()) + dispatch(openModal(MODAL_EMBED, { + url, + })) + }, + + onClosePopover: () => dispatch(closePopover()), }) export default @@ -199,6 +212,8 @@ class StatusOptionsPopover extends ImmutablePureComponent { onPin: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, onFetchGroupRelationships: PropTypes.func.isRequired, + onOpenEmbedModal: PropTypes.func.isRequired, + onClosePopover: PropTypes.func.isRequired, isXS: PropTypes.bool, } @@ -262,6 +277,30 @@ class StatusOptionsPopover extends ImmutablePureComponent { 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.props.onClosePopover() + } + render() { const { status, @@ -274,6 +313,7 @@ class StatusOptionsPopover extends ImmutablePureComponent { 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 = [] @@ -372,6 +412,25 @@ class StatusOptionsPopover extends ImmutablePureComponent { } } + 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, + }) + return ( ({ - onClosePopover: () => dispatch(closePopover(POPOVER_STATUS_SHARE)), - onOpenEmbedModal(url) { - dispatch(openModal(MODAL_EMBED, { - url, - })) - }, -}); - -export default -@injectIntl -@connect(null, mapDispatchToProps) -class StatusSharePopover extends ImmutablePureComponent { - - static propTypes = { - status: ImmutablePropTypes.map, - intl: PropTypes.object.isRequired, - onClosePopover: PropTypes.func.isRequired, - onOpenEmbedModal: PropTypes.func.isRequired, - isXS: PropTypes.bool, - } - - handleOnOpenEmbedModal = () => { - this.props.onOpenEmbedModal(this.props.status.get('url')) - this.props.onClosePopover() - } - - 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.props.onClosePopover() - } - - render() { - const { intl, status, isXS } = this.props - - const mailToHref = !status ? undefined : `mailto:?subject=Gab&body=${status.get('url')}` - - return ( - - - - ) - } -} \ No newline at end of file diff --git a/app/javascript/gabsocial/components/status.js b/app/javascript/gabsocial/components/status.js index d7fb6180..4baf2f03 100644 --- a/app/javascript/gabsocial/components/status.js +++ b/app/javascript/gabsocial/components/status.js @@ -77,6 +77,7 @@ class Status extends ImmutablePureComponent { onClick: PropTypes.func, onReply: PropTypes.func, onRepost: PropTypes.func, + onQuote: PropTypes.func, onFavorite: PropTypes.func, onMention: PropTypes.func, onOpenMedia: PropTypes.func, @@ -291,6 +292,10 @@ class Status extends ImmutablePureComponent { this.props.onReply(status || this._properStatus(), this.context.router, true) } + handleOnQuote = (status) => { + this.props.onQuote(status || this._properStatus(), this.context.router) + } + handleHotkeyFavorite = () => { this.props.onFavorite(this._properStatus()) } @@ -539,6 +544,7 @@ class Status extends ImmutablePureComponent { onShare={this.props.onShare} onOpenLikes={this.props.onOpenLikes} onOpenReposts={this.props.onOpenReposts} + onQuote={this.handleOnQuote} /> } diff --git a/app/javascript/gabsocial/components/status_action_bar.js b/app/javascript/gabsocial/components/status_action_bar.js index 126e8806..dc70169f 100644 --- a/app/javascript/gabsocial/components/status_action_bar.js +++ b/app/javascript/gabsocial/components/status_action_bar.js @@ -5,15 +5,11 @@ import { NavLink } from 'react-router-dom' import { compactMode } from '../initial_state' import Text from './text' import StatusActionBarItem from './status_action_bar_item' -import { - CX, - BREAKPOINT_EXTRA_SMALL, -} from '../constants' -import Responsive from '../features/ui/util/responsive_component' +import { CX } from '../constants' const messages = defineMessages({ comment: { id: 'status.comment', defaultMessage: 'Comment' }, - share: { id: 'status.share', defaultMessage: 'Share' }, + quote: { id: 'status.quote', defaultMessage: 'Quote' }, repost: { id: 'status.repost', defaultMessage: 'Repost' }, cannot_repost: { id: 'status.cannot_repost', defaultMessage: 'This post cannot be reposted' }, like: { id: 'status.like', defaultMessage: 'Like' }, @@ -33,7 +29,7 @@ class StatusActionBar extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, onFavorite: PropTypes.func.isRequired, - onShare: PropTypes.func.isRequired, + onQuote: PropTypes.func.isRequired, onReply: PropTypes.func.isRequired, onRepost: PropTypes.func.isRequired, status: ImmutablePropTypes.map.isRequired, @@ -51,12 +47,12 @@ class StatusActionBar extends ImmutablePureComponent { this.props.onFavorite(this.props.status) } - handleRepostClick = (e) => { - this.props.onRepost(this.repostButton, this.props.status, e) + handleRepostClick = () => { + this.props.onRepost(this.props.status) } - handleShareClick = () => { - this.props.onShare(this.shareButton, this.props.status) + handleQuoteClick = () => { + this.props.onQuote(this.props.status) } openLikesList = () => { @@ -71,10 +67,6 @@ class StatusActionBar extends ImmutablePureComponent { this.repostButton = n } - setShareButton = (n) => { - this.shareButton = n - } - render() { const { status, intl } = this.props @@ -184,14 +176,13 @@ class StatusActionBar extends ImmutablePureComponent { buttonRef={this.setRepostButton} onClick={this.handleRepostClick} /> - - - + diff --git a/app/javascript/gabsocial/constants.js b/app/javascript/gabsocial/constants.js index a205b67e..50c055d1 100644 --- a/app/javascript/gabsocial/constants.js +++ b/app/javascript/gabsocial/constants.js @@ -24,11 +24,9 @@ export const POPOVER_EMOJI_PICKER = 'EMOJI_PICKER' export const POPOVER_GROUP_OPTIONS = 'GROUP_OPTIONS' export const POPOVER_NAV_SETTINGS = 'NAV_SETTINGS' export const POPOVER_PROFILE_OPTIONS = 'PROFILE_OPTIONS' -export const POPOVER_REPOST_OPTIONS = 'REPOST_OPTIONS' export const POPOVER_SEARCH = 'SEARCH' export const POPOVER_SIDEBAR_MORE = 'SIDEBAR_MORE' export const POPOVER_STATUS_OPTIONS = 'STATUS_OPTIONS' -export const POPOVER_STATUS_SHARE = 'STATUS_SHARE' export const POPOVER_STATUS_VISIBILITY = 'STATUS_VISIBILITY' export const POPOVER_USER_INFO = 'USER_INFO' diff --git a/app/javascript/gabsocial/containers/status_container.js b/app/javascript/gabsocial/containers/status_container.js index a01df4e4..3b69d38d 100644 --- a/app/javascript/gabsocial/containers/status_container.js +++ b/app/javascript/gabsocial/containers/status_container.js @@ -1,8 +1,13 @@ import { FormattedMessage } from 'react-intl' -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + fromJS, +} from 'immutable'; import { replyCompose, mentionCompose, + quoteCompose, } from '../actions/compose'; import { repost, @@ -18,7 +23,14 @@ import { } from '../actions/statuses'; import { openModal } from '../actions/modal'; import { openPopover } from '../actions/popover'; -import { me } from '../initial_state'; +import { + me, + boostModal, +} from '../initial_state' +import { + MODAL_BOOST, + MODAL_CONFIRM, +} from '../constants' import { makeGetStatus } from '../selectors' import Status from '../components/status'; @@ -136,38 +148,6 @@ const mapDispatchToProps = (dispatch) => ({ }) }, - onRepost (targetRef, status, e) { - if (!me) return dispatch(openModal('UNAUTHORIZED')) - - if (e.shiftKey) { - this.onModalRepost(status); - } else { - dispatch(openPopover('REPOST_OPTIONS', { - status, - targetRef, - position: 'top', - })) - } - }, - - onModalRepost (status) { - if (!me) return dispatch(openModal('UNAUTHORIZED')) - - if (status.get('reblogged')) { - dispatch(unrepost(status)); - } else { - dispatch(repost(status)); - } - }, - - onShare(targetRef, status) { - dispatch(openPopover('STATUS_SHARE', { - status, - targetRef, - position: 'top', - })) - }, - onShowRevisions (status) { if (!me) return dispatch(openModal('UNAUTHORIZED')) @@ -228,6 +208,41 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(fetchContext(statusId)) }, + onQuote (status, router) { + if (!me) return dispatch(openModal('UNAUTHORIZED')) + + dispatch((_, getState) => { + const state = getState() + if (state.getIn(['compose', 'text']).trim().length !== 0) { + dispatch(openModal(MODAL_CONFIRM, { + message: , + confirm: , + onConfirm: () => dispatch(quoteCompose(status, router)), + })) + } else { + dispatch(quoteCompose(status, router)) + } + }) + }, + + onRepost (status) { + if (!me) return dispatch(openModal('UNAUTHORIZED')) + + 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)) + } + } + }, }); export default connect(makeMapStateToProps, mapDispatchToProps)(Status); diff --git a/app/javascript/gabsocial/features/ui/util/async_components.js b/app/javascript/gabsocial/features/ui/util/async_components.js index 1c44c632..44f4b70a 100644 --- a/app/javascript/gabsocial/features/ui/util/async_components.js +++ b/app/javascript/gabsocial/features/ui/util/async_components.js @@ -57,7 +57,6 @@ export function Notifications() { return import(/* webpackChunkName: "features/n export function ProfileOptionsPopover() { return import(/* webpackChunkName: "components/profile_options_popover" */'../../../components/popover/profile_options_popover') } export function ProUpgradeModal() { return import(/* webpackChunkName: "components/pro_upgrade_modal" */'../../../components/modal/pro_upgrade_modal') } export function ReportModal() { return import(/* webpackChunkName: "modals/report_modal" */'../../../components/modal/report_modal') } -export function RepostOptionsPopover() { return import(/* webpackChunkName: "components/repost_options_popover" */'../../../components/popover/repost_options_popover') } export function Search() { return import(/*webpackChunkName: "features/search" */'../../search') } export function Status() { return import(/* webpackChunkName: "components/status" */'../../../components/status') } export function StatusFeature() { return import(/* webpackChunkName: "features/status" */'../../status') } @@ -69,7 +68,6 @@ export function StatusReposts() { return import(/* webpackChunkName: "features/s export function StatusLikesModal() { return import(/* webpackChunkName: "modals/status_likes_modal" */'../../../components/modal/status_likes_modal') } export function StatusRepostsModal() { return import(/* webpackChunkName: "modals/status_reposts_modal" */'../../../components/modal/status_reposts_modal') } export function StatusRevisionsModal() { return import(/* webpackChunkName: "modals/status_revisions_modal" */'../../../components/modal/status_revisions_modal') } -export function StatusSharePopover() { return import(/* webpackChunkName: "components/status_share_popover" */'../../../components/popover/status_share_popover') } export function StatusVisibilityPopover() { return import(/* webpackChunkName: "components/status_visibility_popover" */'../../../components/popover/status_visibility_popover') } export function UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_modal') } export function UnfollowModal() { return import(/* webpackChunkName: "components/unfollow_modal" */'../../../components/modal/unfollow_modal') }