diff --git a/ui/component/claimRepostButton/index.js b/ui/component/claimRepostButton/index.js new file mode 100644 index 000000000..6331ab9a4 --- /dev/null +++ b/ui/component/claimRepostButton/index.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { doOpenModal } from 'redux/actions/app'; +import ClaimRepostButton from './view'; + +const perform = { + doOpenModal, +}; + +export default connect(null, perform)(ClaimRepostButton); diff --git a/ui/component/claimRepostButton/view.jsx b/ui/component/claimRepostButton/view.jsx new file mode 100644 index 000000000..c9dfc85e0 --- /dev/null +++ b/ui/component/claimRepostButton/view.jsx @@ -0,0 +1,32 @@ +// @flow +import * as MODALS from 'constants/modal_types'; +import * as ICONS from 'constants/icons'; +import React from 'react'; +import classnames from 'classnames'; +import Button from 'component/button'; +import Tooltip from 'component/common/tooltip'; + +type Props = { + uri: string, + fileAction?: boolean, + doOpenModal: (string, {}) => void, +}; + +export default function ClaimRepostButton(props: Props) { + const { uri, fileAction, doOpenModal } = props; + + console.log('uri', uri); + + return ( + + doOpenModal(MODALS.REPOST, { uri })} + /> + + ); +} diff --git a/ui/component/fileActions/view.jsx b/ui/component/fileActions/view.jsx index f10341a60..1e008d0f9 100644 --- a/ui/component/fileActions/view.jsx +++ b/ui/component/fileActions/view.jsx @@ -11,6 +11,7 @@ import * as COLLECTIONS_CONSTS from 'constants/collections'; import * as RENDER_MODES from 'constants/file_render_modes'; import ClaimSupportButton from 'component/claimSupportButton'; import ClaimCollectionAddButton from 'component/claimCollectionAddButton'; +import ClaimRepostButton from 'component/claimRepostButton'; import { useHistory } from 'react-router'; import FileReactions from 'component/fileReactions'; import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button'; @@ -102,6 +103,7 @@ export default function FileActions(props: Props) { } }, [downloadClicked, streamingUrl, fileName]); + // TODO: don't want to redirect. need to instead show modal function handleRepostClick() { if (!hasChannels) { doClearPlayingUri(); @@ -119,7 +121,9 @@ export default function FileActions(props: Props) { - {!hideRepost && !isMobile && !isLivestreamClaim && ( + + + {/* {!hideRepost && !isMobile && !isLivestreamClaim && ( - )} + )} */} void, doClearRepostError: () => void, doRepost: (StreamRepostOptions) => Promise<*>, + doHideModal: () => void, title: string, claim?: StreamClaim, enteredContentClaim?: StreamClaim, @@ -36,7 +35,6 @@ type Props = { setRepostUri: (string) => void, setContentUri: (string) => void, doCheckPendingClaims: () => void, - redirectUri?: string, passedRepostAmount: number, enteredRepostAmount: number, isResolvingPassedRepost: boolean, @@ -48,10 +46,10 @@ type Props = { function RepostCreate(props: Props) { const { - redirectUri, doToast, doClearRepostError, doRepost, + doHideModal, claim, enteredContentClaim, balance, @@ -83,7 +81,6 @@ function RepostCreate(props: Props) { 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}/`}`; @@ -255,27 +252,27 @@ function RepostCreate(props: Props) { const repostClaimId = contentClaimId || enteredClaimId; - const getRedirect = (entered, passed, redirect) => { - if (redirect) { - return redirect; - } else if (entered) { - try { - const { claimName } = parseURI(entered); - return claimName ? `/${claimName}` : '/'; - } catch (e) { - return '/'; - } - } else if (passed) { - try { - const { claimName } = parseURI(passed); - return claimName ? `/${claimName}` : '/'; - } catch (e) { - return '/'; - } - } else { - return '/'; - } - }; + // const getRedirect = (entered, passed, redirect) => { + // if (redirect) { + // return redirect; + // } else if (entered) { + // try { + // const { claimName } = parseURI(entered); + // return claimName ? `/${claimName}` : '/'; + // } catch (e) { + // return '/'; + // } + // } else if (passed) { + // try { + // const { claimName } = parseURI(passed); + // return claimName ? `/${claimName}` : '/'; + // } catch (e) { + // return '/'; + // } + // } else { + // return '/'; + // } + // }; function handleSubmit() { if (enteredRepostName && repostBid && repostClaimId) { @@ -292,14 +289,14 @@ function RepostCreate(props: Props) { linkText: __('Uploads'), linkTarget: '/uploads', }); - replace(getRedirect(contentUri, uri, redirectUri)); + doHideModal(); }); } } function cancelIt() { doClearRepostError(); - goBack(); + doHideModal(); } if (fetchingMyChannels) { @@ -312,12 +309,12 @@ function RepostCreate(props: Props) { return ( <> - - + {uri && ( diff --git a/ui/component/searchTopClaim/index.js b/ui/component/searchTopClaim/index.js index df59e36a0..e9bd83352 100644 --- a/ui/component/searchTopClaim/index.js +++ b/ui/component/searchTopClaim/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { doClearPublish, doPrepareEdit } from 'redux/actions/publish'; import { doResolveUris } from 'redux/actions/claims'; +import { doOpenModal } from 'redux/actions/app'; import { selectPendingIds, makeSelectClaimForUri } from 'redux/selectors/claims'; import { makeSelectWinningUriForQuery, selectIsResolvingWinningUri } from 'redux/selectors/search'; import SearchTopClaim from './view'; @@ -25,6 +26,7 @@ const perform = (dispatch) => ({ dispatch(push(`/$/${PAGES.UPLOAD}`)); }, doResolveUris: (uris) => dispatch(doResolveUris(uris)), + doOpenModal: (id, props) => dispatch(doOpenModal(id, props)), }); export default connect(select, perform)(SearchTopClaim); diff --git a/ui/component/searchTopClaim/view.jsx b/ui/component/searchTopClaim/view.jsx index 12c1fd672..8f540fc12 100644 --- a/ui/component/searchTopClaim/view.jsx +++ b/ui/component/searchTopClaim/view.jsx @@ -1,6 +1,7 @@ // @flow import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; +import * as MODALS from 'constants/modal_types'; import React from 'react'; import { parseURI } from 'util/lbryURI'; import ClaimPreview from 'component/claimPreview'; @@ -8,11 +9,11 @@ import Button from 'component/button'; import ClaimEffectiveAmount from 'component/claimEffectiveAmount'; import ClaimRepostAuthor from 'component/claimRepostAuthor'; import I18nMessage from 'component/i18nMessage'; -import { useHistory } from 'react-router'; import LbcSymbol from 'component/common/lbc-symbol'; import { DOMAIN } from 'config'; type Props = { + doOpenModal: (string, {}) => void, query: string, winningUri: ?string, doResolveUris: (Array) => void, @@ -28,6 +29,7 @@ type Props = { export default function SearchTopClaim(props: Props) { const { doResolveUris, + doOpenModal, query = '', winningUri, winningClaim, @@ -38,7 +40,6 @@ export default function SearchTopClaim(props: Props) { isSearching, } = props; const uriFromQuery = `lbry://${query}`; - const { push } = useHistory(); let name; let channelUriFromQuery; let winningUriIsChannel; @@ -115,13 +116,7 @@ export default function SearchTopClaim(props: Props) { push(`/$/${PAGES.REPOST_NEW}${name ? `?to=${name}` : ''}`)} - label={__('Repost')} - /> - ), + repost: doOpenModal(MODALS.REPOST, {})} label={__('Repost')} />, publish: ( beginPublish(name)} label={__('publish')} /> diff --git a/ui/constants/modal_types.js b/ui/constants/modal_types.js index 9c2842722..0f8bc6463 100644 --- a/ui/constants/modal_types.js +++ b/ui/constants/modal_types.js @@ -20,6 +20,7 @@ export const AFFIRM_PURCHASE = 'affirm_purchase'; export const CONFIRM_CLAIM_REVOKE = 'confirm_claim_revoke'; export const SEND_TIP = 'send_tip'; export const CONFIRM_SEND_TIP = 'confirm_send_tip'; +export const REPOST = 'repost'; export const SOCIAL_SHARE = 'social_share'; export const PUBLISH = 'publish'; export const PUBLISH_PREVIEW = 'publish_preview'; diff --git a/ui/modal/modalRepost/index.js b/ui/modal/modalRepost/index.js new file mode 100644 index 000000000..fc0bc0de7 --- /dev/null +++ b/ui/modal/modalRepost/index.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; +import { doHideModal } from 'redux/actions/app'; +import { doResolveUri } from 'redux/actions/claims'; +import ModalRepost from './view'; + +const perform = (dispatch) => ({ + closeModal: () => dispatch(doHideModal()), + resolveUri: (uri) => dispatch(doResolveUri(uri)), +}); + +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..6990976dc --- /dev/null +++ b/ui/modal/modalRepost/view.jsx @@ -0,0 +1,62 @@ +// @flow +import React from 'react'; +import { useHistory } from 'react-router'; +import Modal from 'modal/modal'; +import RepostCreate from 'component/repostCreate'; +import useThrottle from 'effects/use-throttle'; + +type Props = { + uri: string, + closeModal: () => void, + resolveUri: (string) => void, +}; + +function ModalRepost(props: Props) { + const { uri, closeModal, resolveUri } = props; + + const { + location: { search }, + } = useHistory(); + + const urlParams = new URLSearchParams(search); + const param = urlParams.get('name') || urlParams.get('q'); + const repostTo = param && param[0] === '@' ? param.slice(1) : param; + + const [contentUri, setContentUri] = React.useState(''); + const [repostUri, setRepostUri] = React.useState(''); + const throttledContentValue = useThrottle(contentUri, 500); + const throttledRepostValue = useThrottle(repostUri, 500); + + React.useEffect(() => { + if (throttledContentValue) { + resolveUri(throttledContentValue); + } + }, [throttledContentValue, resolveUri]); + + React.useEffect(() => { + if (throttledRepostValue) { + resolveUri(throttledRepostValue); + } + }, [throttledRepostValue, resolveUri]); + + React.useEffect(() => { + if (repostTo) { + resolveUri(repostTo); + } + }, [repostTo, resolveUri]); + + return ( + + + + ); +} + +export default ModalRepost; diff --git a/ui/modal/modalRouter/view.jsx b/ui/modal/modalRouter/view.jsx index 1eacad567..46c85a640 100644 --- a/ui/modal/modalRouter/view.jsx +++ b/ui/modal/modalRouter/view.jsx @@ -39,6 +39,7 @@ const MAP = Object.freeze({ [MODALS.PHONE_COLLECTION]: lazyImport(() => import('modal/modalPhoneCollection' /* webpackChunkName: "modalPhoneCollection" */)), [MODALS.PUBLISH]: lazyImport(() => import('modal/modalPublish' /* webpackChunkName: "modalPublish" */)), [MODALS.PUBLISH_PREVIEW]: lazyImport(() => import('modal/modalPublishPreview' /* webpackChunkName: "modalPublishPreview" */)), + [MODALS.REPOST]: lazyImport(() => import('modal/modalRepost' /* webpackChunkName: "modalRepost" */)), [MODALS.REWARD_GENERATED_CODE]: lazyImport(() => import('modal/modalRewardCode' /* webpackChunkName: "modalRewardCode" */)), [MODALS.SEND_TIP]: lazyImport(() => import('modal/modalSendTip' /* webpackChunkName: "modalSendTip" */)), [MODALS.SET_REFERRER]: lazyImport(() => import('modal/modalSetReferrer' /* webpackChunkName: "modalSetReferrer" */)), diff --git a/ui/page/top/index.js b/ui/page/top/index.js index da7f3cd18..013740872 100644 --- a/ui/page/top/index.js +++ b/ui/page/top/index.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import TopPage from './view'; import { doClearPublish, doPrepareEdit } from 'redux/actions/publish'; import { doResolveUris } from 'redux/actions/claims'; +import { doOpenModal } from 'redux/actions/app'; import { push } from 'connected-react-router'; import * as PAGES from 'constants/pages'; @@ -22,6 +23,7 @@ const perform = (dispatch) => ({ dispatch(push(`/$/${PAGES.UPLOAD}`)); }, doResolveUris: (uris) => dispatch(doResolveUris(uris)), + doOpenModal: (id, props) => dispatch(doOpenModal(id, props)), }); export default connect(select, perform)(TopPage); diff --git a/ui/page/top/view.jsx b/ui/page/top/view.jsx index 1dfa02161..0cdfd0957 100644 --- a/ui/page/top/view.jsx +++ b/ui/page/top/view.jsx @@ -7,16 +7,17 @@ import ClaimEffectiveAmount from 'component/claimEffectiveAmount'; import SearchTopClaim from 'component/searchTopClaim'; import * as CS from 'constants/claim_search'; import Button from 'component/button'; -import * as PAGES from 'constants/pages'; +import * as MODALS from 'constants/modal_types'; import { SIMPLE_SITE } from 'config'; type Props = { name: string, beginPublish: (string) => void, + doOpenModal: (string, {}) => void, }; function TopPage(props: Props) { - const { name, beginPublish } = props; + const { name, beginPublish, doOpenModal } = props; const [channelActive, setChannelActive] = React.useState(false); // if the query was actually '@name', still offer repost for 'name' const queryName = name[0] === '@' ? name.slice(1) : name; @@ -30,7 +31,8 @@ function TopPage(props: Props) { streamType={SIMPLE_SITE ? CS.CONTENT_ALL : undefined} meta={ - + {/* */} + doOpenModal(MODALS.REPOST, {})} label={__('Repost Here')} /> beginPublish(queryName)} label={__('Publish Here')} /> } diff --git a/ui/scss/init/_gui.scss b/ui/scss/init/_gui.scss index a5f34d4a4..f152fa875 100644 --- a/ui/scss/init/_gui.scss +++ b/ui/scss/init/_gui.scss @@ -1117,6 +1117,13 @@ img { } .repost-wrapper { + // Repost now uses a modal. Lock the width because the bid message + // keeps changing as you type the desired name. + min-width: var(--modal-width); + @media (max-width: $breakpoint-small) { + min-width: 100%; + } + .claim-preview__wrapper { background-color: rgba(var(--color-header-background-base), 0.6); &:hover {