diff --git a/ui/component/claimRepostButton/index.js b/ui/component/claimRepostButton/index.js new file mode 100644 index 000000000..247a1ed18 --- /dev/null +++ b/ui/component/claimRepostButton/index.js @@ -0,0 +1,6 @@ +import { connect } from 'react-redux'; +import { doOpenModal } from 'redux/actions/app'; +import { doToast } from 'redux/actions/notifications'; +import ClaimReportButton from './view'; + +export default connect(null, { doOpenModal, doToast })(ClaimReportButton); diff --git a/ui/component/claimRepostButton/view.jsx b/ui/component/claimRepostButton/view.jsx new file mode 100644 index 000000000..57aa58850 --- /dev/null +++ b/ui/component/claimRepostButton/view.jsx @@ -0,0 +1,44 @@ +// @flow +import { SITE_NAME } from 'config'; +import * as MODALS from 'constants/modal_types'; +import * as ICONS from 'constants/icons'; +import React from 'react'; +import Button from 'component/button'; + +type Props = { + uri: string, + claim: StreamClaim, + hasChannels: boolean, + doOpenModal: (string, {}) => void, + doToast: ({ message: string }) => void, +}; + +export default function ClaimRepostButton(props: Props) { + const { uri, claim, hasChannels, doOpenModal, doToast } = props; + const [contentUri, setContentUri] = React.useState(''); + const [repostUri, setRepostUri] = React.useState(''); + + return ( + <Button + button="alt" + className="button--file-action" + icon={ICONS.REPOST} + label={ + claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost') + } + description={__('Repost')} + requiresAuth={IS_WEB} + onClick={() => { + if (!hasChannels) { + doToast({ + message: __('A channel is required to repost on %SITE_NAME%', { SITE_NAME }), + linkText: __('Create Channel'), + linkTarget: '/channel/new', + }); + } else { + doOpenModal(MODALS.REPOST, { uri, contentUri, setContentUri, repostUri, setRepostUri, isModal: true }); + } + }} + /> + ); +} diff --git a/ui/component/fileActions/view.jsx b/ui/component/fileActions/view.jsx index 303737bfa..f2c9aeb63 100644 --- a/ui/component/fileActions/view.jsx +++ b/ui/component/fileActions/view.jsx @@ -1,5 +1,5 @@ // @flow -import { SITE_NAME, ENABLE_FILE_REACTIONS } from 'config'; +import { ENABLE_FILE_REACTIONS } from 'config'; import * as PAGES from 'constants/pages'; import * as MODALS from 'constants/modal_types'; import * as ICONS from 'constants/icons'; @@ -16,6 +16,7 @@ import { useHistory } from 'react-router'; import FileReactions from 'component/fileReactions'; import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button'; import Icon from 'component/common/icon'; +import ClaimRepostButton from 'component/claimRepostButton'; type Props = { uri: string, @@ -27,8 +28,6 @@ type Props = { costInfo: ?{ cost: number }, renderMode: string, myChannels: ?Array<ChannelClaim>, - doToast: ({ message: string }) => void, - clearPlayingUri: () => void, hideRepost?: boolean, reactionsDisabled: boolean, download: (string) => void, @@ -46,14 +45,12 @@ function FileActions(props: Props) { renderMode, prepareEdit, myChannels, - clearPlayingUri, - doToast, hideRepost, reactionsDisabled, } = props; const { push, - location: { pathname, search }, + location: { search }, } = useHistory(); const isMobile = useIsMobile(); const webShareable = costInfo && costInfo.cost === 0 && RENDER_MODES.WEB_SHAREABLE_MODES.includes(renderMode); @@ -82,33 +79,13 @@ function FileActions(props: Props) { const urlParams = new URLSearchParams(search); const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); - function handleRepostClick() { - if (!hasChannels) { - clearPlayingUri(); - push(`/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`); - doToast({ message: __('A channel is required to repost on %SITE_NAME%', { SITE_NAME }) }); - } else { - push(`/$/${PAGES.REPOST_NEW}?from=${encodeURIComponent(uri)}&redirect=${pathname}`); - } - } - const lhsSection = ( <> {ENABLE_FILE_REACTIONS && !reactionsDisabled && <FileReactions uri={uri} />} <ClaimSupportButton uri={uri} fileAction /> <ClaimCollectionAddButton uri={uri} fileAction /> {!hideRepost && ( - <Button - button="alt" - className="button--file-action" - icon={ICONS.REPOST} - label={ - claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost') - } - description={__('Repost')} - requiresAuth={IS_WEB} - onClick={handleRepostClick} - /> + <ClaimRepostButton uri={uri} claim={claim} hasChannels={hasChannels} /> )} <Button className="button--file-action" diff --git a/ui/component/repostCreate/view.jsx b/ui/component/repostCreate/view.jsx index e8ccc9fb1..ce2a39841 100644 --- a/ui/component/repostCreate/view.jsx +++ b/ui/component/repostCreate/view.jsx @@ -23,6 +23,7 @@ type Props = { doToast: ({ message: string }) => void, doClearRepostError: () => void, doRepost: (StreamRepostOptions) => Promise<*>, + doHideModal: () => void, title: string, claim?: StreamClaim, enteredContentClaim?: StreamClaim, @@ -33,11 +34,7 @@ type Props = { reposting: boolean, uri: string, name: string, - contentUri: string, - setRepostUri: (string) => void, - setContentUri: (string) => void, doCheckPendingClaims: () => void, - redirectUri?: string, passedRepostAmount: number, enteredRepostAmount: number, isResolvingPassedRepost: boolean, @@ -45,11 +42,16 @@ type Props = { activeChannelClaim: ?ChannelClaim, fetchingMyChannels: boolean, incognito: boolean, + contentUri: string, + setContentUri: (string) => void, + repostUri: string, + setRepostUri: (string) => void, + isModal: boolean, + redirectUri?: string, }; function RepostCreate(props: Props) { const { - redirectUri, doToast, doClearRepostError, doRepost, @@ -60,9 +62,6 @@ function RepostCreate(props: Props) { doCheckPublishNameAvailability, uri, // ?from name, // ?to - contentUri, - setRepostUri, - setContentUri, doCheckPendingClaims, enteredRepostAmount, passedRepostAmount, @@ -71,6 +70,12 @@ function RepostCreate(props: Props) { activeChannelClaim, fetchingMyChannels, incognito, + doHideModal, + contentUri, + setContentUri, + setRepostUri, + isModal, + redirectUri, } = props; const defaultName = name || (claim && claim.name) || ''; @@ -83,8 +88,8 @@ function RepostCreate(props: Props) { const [available, setAvailable] = React.useState(true); const [enteredContent, setEnteredContentUri] = React.useState(undefined); const [contentError, setContentError] = React.useState(''); - const { replace, goBack } = useHistory(); + const resolvingRepost = isResolvingEnteredRepost || isResolvingPassedRepost; const repostUrlName = `lbry://${incognito || !activeChannelClaim ? '' : `${activeChannelClaim.name}/`}`; @@ -293,14 +298,22 @@ function RepostCreate(props: Props) { linkText: __('Uploads'), linkTarget: '/uploads', }); - replace(getRedirect(contentUri, uri, redirectUri)); + if (isModal) { + doHideModal(); + } else { + replace(getRedirect(contentUri, uri, redirectUri)); + } }); } } function cancelIt() { doClearRepostError(); - goBack(); + if (isModal) { + doHideModal(); + } else { + goBack(); + } } if (fetchingMyChannels) { diff --git a/ui/constants/modal_types.js b/ui/constants/modal_types.js index 2355f3087..e390af9e2 100644 --- a/ui/constants/modal_types.js +++ b/ui/constants/modal_types.js @@ -19,6 +19,7 @@ export const AFFIRM_PURCHASE = 'affirm_purchase'; export const CONFIRM_CLAIM_REVOKE = 'confirm_claim_revoke'; export const FIRST_SUBSCRIPTION = 'firstSubscription'; export const SEND_TIP = 'send_tip'; +export const REPOST = 'repost'; export const CONFIRM_SEND_TIP = 'confirm_send_tip'; export const SOCIAL_SHARE = 'social_share'; export const PUBLISH = 'publish'; diff --git a/ui/modal/modalRepost/index.js b/ui/modal/modalRepost/index.js new file mode 100644 index 000000000..6e3890cac --- /dev/null +++ b/ui/modal/modalRepost/index.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { doHideModal } from 'redux/actions/app'; +import ModalRepost from './view'; + +const perform = dispatch => ({ + closeModal: () => dispatch(doHideModal()), +}); + +export default connect(null, perform)(ModalRepost); diff --git a/ui/modal/modalRepost/view.jsx b/ui/modal/modalRepost/view.jsx new file mode 100644 index 000000000..292b27007 --- /dev/null +++ b/ui/modal/modalRepost/view.jsx @@ -0,0 +1,32 @@ +// @flow +import React from 'react'; +import { Modal } from 'modal/modal'; +import RepostCreate from 'component/repostCreate'; +import Card from 'component/common/card'; + +type Props = { + closeModal: () => void, + uri: string, + name: string, + contentUri: string, + setContentUri: () => void, + repostUri: string, + setRepostUri: () => void, +} + +class ModalRepost extends React.PureComponent<Props> { + render() { + const { closeModal, uri, name, contentUri, setContentUri, repostUri, setRepostUri } = this.props; + + return ( + <Modal onAborted={closeModal} isOpen type="card"> + <Card + title={__('Repost')} + actions={<RepostCreate isModal uri={uri} name={name} onCancel={closeModal} contentUri={contentUri} setContentUri={setContentUri} repostUri={repostUri} setRepostUri={setRepostUri} />} + /> + </Modal> + ); + } +} + +export default ModalRepost; diff --git a/ui/modal/modalRouter/view.jsx b/ui/modal/modalRouter/view.jsx index 510c51bcd..353d39e22 100644 --- a/ui/modal/modalRouter/view.jsx +++ b/ui/modal/modalRouter/view.jsx @@ -53,6 +53,7 @@ import ModalRemoveFile from 'modal/modalRemoveFile'; import ModalRevokeClaim from 'modal/modalRevokeClaim'; import ModalRewardCode from 'modal/modalRewardCode'; import ModalSendTip from 'modal/modalSendTip'; +import ModalRepost from 'modal/modalRepost'; import ModalSetReferrer from 'modal/modalSetReferrer'; import ModalSignOut from 'modal/modalSignOut'; import ModalSocialShare from 'modal/modalSocialShare'; @@ -124,6 +125,8 @@ function ModalRouter(props: Props) { return ModalFirstSubscription; case MODALS.SEND_TIP: return ModalSendTip; + case MODALS.REPOST: + return ModalRepost; case MODALS.SOCIAL_SHARE: return ModalSocialShare; case MODALS.PUBLISH: