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