Added quote posting to status bar, Removed share from status bar

• 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
This commit is contained in:
mgabdev 2020-05-27 01:13:46 -04:00
parent 2824e9d3b9
commit 0364a4000b
10 changed files with 155 additions and 288 deletions

@ -0,0 +1,24 @@
const QuoteIcon = ({
className = '',
size = '16px',
title = 'Quote',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={size}
height={size}
viewBox='0 0 48 48'
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 16.67 3 L 6 3 C 2.69 3 0 6.03 0 9.75 L 0 20.25 C 0 23.97 2.69 27 6 27 L 10.67 27 L 10.67 27.75 C 10.67 29.82 9.18 31.5 7.33 31.5 L 6.67 31.5 C 4.46 31.5 2.67 33.52 2.67 36 L 2.67 40.5 C 2.67 42.98 4.46 45 6.67 45 L 7.33 45 C 15.79 45 22.67 37.27 22.67 27.75 L 22.67 9.75 C 22.67 6.03 19.98 3 16.67 3 Z M 18.67 27.75 C 18.67 34.78 13.58 40.5 7.33 40.5 L 6.67 40.5 L 6.67 36 L 7.33 36 C 11.38 36 14.67 32.3 14.67 27.75 L 14.67 22.5 L 6 22.5 C 4.9 22.5 4 21.49 4 20.25 L 4 9.75 C 4 8.51 4.9 7.5 6 7.5 L 16.67 7.5 C 17.77 7.5 18.67 8.51 18.67 9.75 Z M 42 3 L 31.33 3 C 28.02 3 25.33 6.03 25.33 9.75 L 25.33 20.25 C 25.33 23.97 28.02 27 31.33 27 L 36 27 L 36 27.75 C 36 29.82 34.51 31.5 32.67 31.5 L 32 31.5 C 29.79 31.5 28 33.52 28 36 L 28 40.5 C 28 42.98 29.79 45 32 45 L 32.67 45 C 41.13 45 48 37.27 48 27.75 L 48 9.75 C 48 6.03 45.31 3 42 3 Z M 44 27.75 C 44 34.78 38.92 40.5 32.67 40.5 L 32 40.5 L 32 36 L 32.67 36 C 36.71 36 40 32.3 40 27.75 L 40 22.5 L 31.33 22.5 C 30.23 22.5 29.33 21.49 29.33 20.25 L 29.33 9.75 C 29.33 8.51 30.23 7.5 31.33 7.5 L 42 7.5 C 43.1 7.5 44 8.51 44 9.75 Z M 44 27.75' />
</g>
</svg>
)
export default QuoteIcon

@ -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,

@ -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 (
<PopoverLayout width={220} isXS={isXS}>
<List
scrollKey='repost_options'
size='large'
items={[
{
hideArrow: true,
icon: 'repost',
title: intl.formatMessage(!alreadyReposted ? messages.repost : messages.removeRepost),
onClick: this.handleOnRepost,
},
{
hideArrow: true,
icon: 'pencil',
title: intl.formatMessage(messages.repostWithComment),
onClick: this.handleOnQuote,
}
]}
/>
</PopoverLayout>
)
}
}

@ -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 (
<PopoverLayout isXS={isXS}>
<List

@ -1,101 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import { closePopover } from '../../actions/popover'
import { openModal } from '../../actions/modal'
import {
MODAL_EMBED,
POPOVER_STATUS_SHARE,
} from '../../constants'
import PopoverLayout from './popover_layout'
import List from '../list'
const messages = defineMessages({
embed: { id: 'status.embed', defaultMessage: 'Embed' },
email: { id: 'status.email', defaultMessage: 'Email this gab' },
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
});
const mapDispatchToProps = (dispatch) => ({
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 (
<PopoverLayout width={220} isXS={isXS}>
<List
size='large'
scrollKey='status_share_options'
items={[
{
icon: 'copy',
hideArrow: true,
title: intl.formatMessage(messages.copy),
onClick: this.handleCopy,
},
{
icon: 'email',
hideArrow: true,
title: intl.formatMessage(messages.email),
href: mailToHref,
},
{
icon: 'code',
hideArrow: true,
title: intl.formatMessage(messages.embed),
onClick: this.handleOnOpenEmbedModal,
},
]}
small
/>
</PopoverLayout>
)
}
}

@ -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}
/>
}

@ -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}
/>
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
<StatusActionBarItem
buttonRef={this.setShareButton}
title={compactMode ? '' : intl.formatMessage(messages.share)}
icon='share'
onClick={this.handleShareClick}
/>
</Responsive>
<StatusActionBarItem
title={intl.formatMessage(messages.quote)}
altTitle={!publicStatus ? intl.formatMessage(messages.cannot_repost) : ''}
icon={!publicStatus ? 'lock' : 'quote'}
disabled={!publicStatus}
onClick={this.handleQuoteClick}
/>
</div>
</div>
</div>

@ -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'

@ -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: <FormattedMessage id='confirmations.quote.message' defaultMessage='Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' />,
confirm: <FormattedMessage id='confirmations.quote.confirm' defaultMessage='Quote' />,
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);

@ -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') }