diff --git a/webapp/package.json b/webapp/package.json index b65755ef14..eee0b6c2d4 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -123,4 +123,4 @@ "type": "git", "url": "https://github.com/decentraland/marketplace.git" } -} +} \ No newline at end of file diff --git a/webapp/src/components/AccountPage/AccountPage.container.ts b/webapp/src/components/AccountPage/AccountPage.container.ts index 26f366a18f..abb649f307 100644 --- a/webapp/src/components/AccountPage/AccountPage.container.ts +++ b/webapp/src/components/AccountPage/AccountPage.container.ts @@ -1,11 +1,10 @@ import { connect } from 'react-redux' -import { replace } from 'connected-react-router' import { isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' import { RootState } from '../../modules/reducer' import { getIsFullscreen, getVendor, getViewAsGuest } from '../../modules/routing/selectors' import { getWallet } from '../../modules/wallet/selectors' import AccountPage from './AccountPage' -import { MapStateProps, MapDispatch, MapDispatchProps, OwnProps } from './AccountPage.types' +import { MapStateProps, OwnProps } from './AccountPage.types' const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => { const { address } = ownProps.match.params @@ -20,8 +19,4 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => { } } -const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onRedirect: path => dispatch(replace(path)) -}) - -export default connect(mapState, mapDispatch)(AccountPage) +export default connect(mapState)(AccountPage) diff --git a/webapp/src/components/AccountPage/AccountPage.tsx b/webapp/src/components/AccountPage/AccountPage.tsx index 385dbf7db3..422e36261a 100644 --- a/webapp/src/components/AccountPage/AccountPage.tsx +++ b/webapp/src/components/AccountPage/AccountPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react' -import { useLocation } from 'react-router-dom' +import { useHistory, useLocation } from 'react-router-dom' import { AddressProvider } from 'decentraland-dapps/dist/containers/AddressProvider' import { t } from 'decentraland-dapps/dist/modules/translation/utils' import { Page, Loader, Center } from 'decentraland-ui' @@ -12,17 +12,18 @@ import AccountBanner from './AccountBanner' import { Props } from './AccountPage.types' import './AccountPage.css' -const AccountPage = ({ addressInUrl, vendor, wallet, isConnecting, viewAsGuest, onRedirect }: Props) => { +const AccountPage = ({ addressInUrl, vendor, wallet, isConnecting, viewAsGuest }: Props) => { const isCurrentAccount = (!addressInUrl || wallet?.address === addressInUrl) && !viewAsGuest const { pathname, search } = useLocation() + const history = useHistory() // Redirect to signIn if trying to access current account without a wallet useEffect(() => { if (!addressInUrl && !isConnecting && !wallet) { - onRedirect(locations.signIn(`${pathname}${search}`)) + history.replace(locations.signIn(`${pathname}${search}`)) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [addressInUrl, isConnecting, wallet, onRedirect]) + }, [addressInUrl, isConnecting, wallet, history]) return ( diff --git a/webapp/src/components/AccountPage/AccountPage.types.ts b/webapp/src/components/AccountPage/AccountPage.types.ts index 7b272cf9d8..39a699d7c4 100644 --- a/webapp/src/components/AccountPage/AccountPage.types.ts +++ b/webapp/src/components/AccountPage/AccountPage.types.ts @@ -1,6 +1,4 @@ import { RouteComponentProps } from 'react-router-dom' -import { CallHistoryMethodAction } from 'connected-react-router' -import { Dispatch } from 'redux' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' import { VendorName } from '../../modules/vendor/types' @@ -13,10 +11,7 @@ export type Props = { isConnecting: boolean isFullscreen?: boolean viewAsGuest: boolean - onRedirect: (path: string) => void } & RouteComponentProps export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch export type OwnProps = RouteComponentProps diff --git a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx index 82ca6140c3..ed22bf2f15 100644 --- a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx +++ b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { replace } from 'connected-react-router' import { Order } from '@dcl/schemas' import { openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { getOpenModals } from 'decentraland-dapps/dist/modules/modal/selectors' @@ -29,8 +28,7 @@ const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProp }) ) : dispatch(openModal('MintNftWithCryptoModal', { item: asset })), - onBuyItemWithCard: item => dispatch(buyItemWithCardRequest(item)), - onRedirect: path => dispatch(replace(path)) + onBuyItemWithCard: item => dispatch(buyItemWithCardRequest(item)) }) export default connect(mapState, mapDispatch)(BuyNFTButtons) diff --git a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx index f85a6e9d87..b3e30baae0 100644 --- a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx +++ b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx @@ -1,5 +1,5 @@ import { memo, useCallback, useMemo } from 'react' -import { useLocation } from 'react-router-dom' +import { useHistory, useLocation } from 'react-router-dom' import { Order } from '@dcl/schemas' import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils' import { Loader } from 'decentraland-ui' @@ -23,8 +23,7 @@ const BuyNFTButtons = ({ isBuyingWithCryptoModalOpen, onBuyWithCrypto, onExecuteOrderWithCard, - onBuyItemWithCard, - onRedirect + onBuyItemWithCard }: Props) => { const analytics = getAnalytics() const location = useLocation() @@ -33,6 +32,7 @@ const BuyNFTButtons = ({ const shouldOpenModal = search.get('buyWithCrypto') return shouldOpenModal }, [location.search]) + const history = useHistory() const handleBuyWithCard = useCallback( (asset: Asset) => { @@ -45,13 +45,13 @@ const BuyNFTButtons = ({ const handleBuyWithCrypto = useCallback( (asset: Asset, order: Order | null) => { if (!isConnecting && !wallet && !isBuyingWithCryptoModalOpen) { - onRedirect(locations.signIn(`${location.pathname}?buyWithCrypto=true`)) + history.replace(locations.signIn(`${location.pathname}?buyWithCrypto=true`)) } else { analytics.track(events.CLICK_BUY_NFT_WITH_CRYPTO) onBuyWithCrypto(asset, order) } }, - [isConnecting, wallet, isBuyingWithCryptoModalOpen, location.pathname, analytics, onRedirect, onBuyWithCrypto] + [isConnecting, wallet, isBuyingWithCryptoModalOpen, location.pathname, analytics, history, onBuyWithCrypto] ) return ( diff --git a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.types.ts b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.types.ts index 73d1d868f6..e7c723621d 100644 --- a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.types.ts +++ b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { Order } from '@dcl/schemas' import { OpenModalAction } from 'decentraland-dapps/dist/modules/modal/actions' @@ -18,14 +17,11 @@ export type Props = { onBuyWithCrypto: (asset: Asset, order?: Order | null) => void onExecuteOrderWithCard: typeof executeOrderWithCardRequest onBuyItemWithCard: typeof buyItemWithCardRequest - onRedirect: (path: string) => void } export type OwnProps = Pick export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch< - ExecuteOrderWithCardRequestAction | BuyItemWithCardRequestAction | OpenModalAction | CallHistoryMethodAction -> +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/AssetPage/YourOffer/YourOffer.container.ts b/webapp/src/components/AssetPage/YourOffer/YourOffer.container.ts index 07d47b7d9a..ca085e3555 100644 --- a/webapp/src/components/AssetPage/YourOffer/YourOffer.container.ts +++ b/webapp/src/components/AssetPage/YourOffer/YourOffer.container.ts @@ -1,8 +1,6 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { cancelBidRequest } from '../../../modules/bid/actions' import { RootState } from '../../../modules/reducer' -import { locations } from '../../../modules/routing/locations' import { getAddress } from '../../../modules/wallet/selectors' import YourOffer from './YourOffer' import { MapStateProps, MapDispatchProps, MapDispatch } from './YourOffer.types' @@ -12,7 +10,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onUpdate: bid => dispatch(push(locations.bid(bid.contractAddress, bid.tokenId))), onCancel: bid => dispatch(cancelBidRequest(bid)) }) diff --git a/webapp/src/components/AssetPage/YourOffer/YourOffer.tsx b/webapp/src/components/AssetPage/YourOffer/YourOffer.tsx index 33697825f2..77f4d6a655 100644 --- a/webapp/src/components/AssetPage/YourOffer/YourOffer.tsx +++ b/webapp/src/components/AssetPage/YourOffer/YourOffer.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react' +import { useHistory } from 'react-router-dom' import classNames from 'classnames' import { Bid, Network } from '@dcl/schemas' import { t } from 'decentraland-dapps/dist/modules/translation/utils' @@ -9,6 +10,7 @@ import iconListings from '../../../images/iconListings.png' import infoIcon from '../../../images/infoIcon.png' import { formatDistanceToNow } from '../../../lib/date' import { formatWeiMANA } from '../../../lib/mana' +import { locations } from '../../../modules/routing/locations' import { bidAPI } from '../../../modules/vendor/decentraland' import Mana from '../../Mana/Mana' import { ManaToFiat } from '../../ManaToFiat' @@ -80,7 +82,8 @@ const ExpirationDate = (props: { bid: Bid }) => { } const YourOffer = (props: Props) => { - const { nft, address, onUpdate, onCancel } = props + const history = useHistory() + const { nft, address, onCancel } = props const [bid, setBid] = useState() const isMobile = useMobileMediaQuery() @@ -124,7 +127,7 @@ const YourOffer = (props: Props) => { - diff --git a/webapp/src/components/AssetPage/YourOffer/YourOffer.types.ts b/webapp/src/components/AssetPage/YourOffer/YourOffer.types.ts index 6c05df60c1..dbb21552b5 100644 --- a/webapp/src/components/AssetPage/YourOffer/YourOffer.types.ts +++ b/webapp/src/components/AssetPage/YourOffer/YourOffer.types.ts @@ -1,5 +1,4 @@ import { Dispatch } from 'react' -import { CallHistoryMethodAction } from 'connected-react-router' import { Bid } from '@dcl/schemas' import { CancelBidRequestAction } from '../../../modules/bid/actions' import { NFT } from '../../../modules/nft/types' @@ -8,12 +7,11 @@ import { VendorName } from '../../../modules/vendor' export type Props = { nft: NFT | null address?: string - onUpdate: (bid: Bid) => void onCancel: (bid: Bid) => void } export type MapStateProps = Pick -export type MapDispatchProps = Pick +export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatch = Dispatch diff --git a/webapp/src/components/Atlas/Atlas.container.ts b/webapp/src/components/Atlas/Atlas.container.ts index 0a224cbd58..1b89d03401 100644 --- a/webapp/src/components/Atlas/Atlas.container.ts +++ b/webapp/src/components/Atlas/Atlas.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { getContract } from '../../modules/contract/selectors' import { getWalletNFTs } from '../../modules/nft/selectors' import { RootState } from '../../modules/reducer' @@ -8,7 +7,7 @@ import { getOnRentNFTsByLessor } from '../../modules/ui/browse/selectors' import { Contract } from '../../modules/vendor/services' import { getWallet } from '../../modules/wallet/selectors' import Atlas from './Atlas' -import { MapStateProps, MapDispatch, MapDispatchProps } from './Atlas.types' +import { MapStateProps } from './Atlas.types' const mapState = (state: RootState): MapStateProps => { const wallet = getWallet(state) @@ -24,8 +23,4 @@ const mapState = (state: RootState): MapStateProps => { } } -const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)) -}) - -export default connect(mapState, mapDispatch)(Atlas) +export default connect(mapState)(Atlas) diff --git a/webapp/src/components/Atlas/Atlas.tsx b/webapp/src/components/Atlas/Atlas.tsx index bcd262eab5..4cab3dcb20 100644 --- a/webapp/src/components/Atlas/Atlas.tsx +++ b/webapp/src/components/Atlas/Atlas.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useHistory } from 'react-router-dom' import classNames from 'classnames' import { NFTCategory, RentalStatus } from '@dcl/schemas' import { t } from 'decentraland-dapps/dist/modules/translation/utils' @@ -18,7 +19,6 @@ const getCoords = (x: number | string, y: number | string) => `${x},${y}` const Atlas: React.FC = (props: Props) => { const { tiles, - onNavigate, isEstate, withNavigation, nfts, @@ -36,6 +36,7 @@ const Atlas: React.FC = (props: Props) => { lastUpdated } = props + const history = useHistory() const [showPopup, setShowPopup] = useState(false) const [isInfoPopupOpen, setIsInfoPopupOpen] = useState(false) const [hoveredTile, setHoveredTile] = useState(null) @@ -167,21 +168,21 @@ const Atlas: React.FC = (props: Props) => { const estates = getContract({ category: NFTCategory.ESTATE }) - estates && onNavigate(locations.nft(estates.address, tile.estate_id)) + estates && history.push(locations.nft(estates.address, tile.estate_id)) } else { try { const land = getContract({ category: NFTCategory.PARCEL }) const tokenId = await nftAPI.fetchTokenId(tile.x, tile.y) - land && onNavigate(locations.nft(land.address, tokenId ?? undefined)) + land && history.push(locations.nft(land.address, tokenId ?? undefined)) } catch (error) { const errorMessage = isErrorWithMessage(error) ? error.message : t('global.unknown_error') console.warn(`Couldn't fetch parcel ${tile.x},${tile.y}: ${errorMessage}`) } } }, - [withNavigation, tiles, getContract, onNavigate] + [withNavigation, tiles, getContract] ) const handleHover = useCallback( diff --git a/webapp/src/components/Atlas/Atlas.types.ts b/webapp/src/components/Atlas/Atlas.types.ts index 0fc22efe48..ab364795f1 100644 --- a/webapp/src/components/Atlas/Atlas.types.ts +++ b/webapp/src/components/Atlas/Atlas.types.ts @@ -1,6 +1,4 @@ import React from 'react' -import { CallHistoryMethodAction } from 'connected-react-router' -import { Dispatch } from 'redux' import { RentalListing } from '@dcl/schemas' import { AtlasTile, AtlasProps } from 'decentraland-ui' import { getContract } from '../../modules/contract/selectors' @@ -35,10 +33,7 @@ export type Props = Partial & { lastUpdated?: Date lastAtlasModifiedDate: Date | null getContract: (query: Partial) => ReturnType - onNavigate: (path: string) => void children?: React.ReactNode } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch diff --git a/webapp/src/components/Bid/Bid.container.ts b/webapp/src/components/Bid/Bid.container.ts index 5d9dfc9b05..e4a6355611 100644 --- a/webapp/src/components/Bid/Bid.container.ts +++ b/webapp/src/components/Bid/Bid.container.ts @@ -1,10 +1,8 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { cancelBidRequest, archiveBid, unarchiveBid, acceptBidRequest, ACCEPT_BID_REQUEST } from '../../modules/bid/actions' import { getLoading } from '../../modules/bid/selectors' import { RootState } from '../../modules/reducer' -import { locations } from '../../modules/routing/locations' import { getArchivedBidIds } from '../../modules/ui/nft/bid/selectors' import { getWallet } from '../../modules/wallet/selectors' import Bid from './Bid' @@ -17,7 +15,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onUpdate: bid => dispatch(push(locations.bid(bid.contractAddress, bid.tokenId))), onCancel: bid => dispatch(cancelBidRequest(bid)), onArchive: bid => dispatch(archiveBid(bid)), onUnarchive: bid => dispatch(unarchiveBid(bid)), diff --git a/webapp/src/components/Bid/Bid.tsx b/webapp/src/components/Bid/Bid.tsx index deb4dd8216..d7dc34970c 100644 --- a/webapp/src/components/Bid/Bid.tsx +++ b/webapp/src/components/Bid/Bid.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useState } from 'react' -import { Link } from 'react-router-dom' +import { Link, useHistory } from 'react-router-dom' import { ethers } from 'ethers' import { T, t } from 'decentraland-dapps/dist/modules/translation/utils' import { Loader, Stats, Button } from 'decentraland-ui' @@ -20,8 +20,8 @@ import { Props } from './Bid.types' import './Bid.css' const Bid = (props: Props) => { - const { bid, wallet, archivedBidIds, onAccept, onArchive, onUnarchive, onCancel, onUpdate, isArchivable, hasImage, isAcceptingBid } = - props + const { bid, wallet, archivedBidIds, onAccept, onArchive, onUnarchive, onCancel, isArchivable, hasImage, isAcceptingBid } = props + const history = useHistory() const isArchived = archivedBidIds.includes(bid.id) const isBidder = !!wallet && addressEquals(wallet.address, bid.bidder) @@ -67,7 +67,7 @@ const Bid = (props: Props) => {
{isBidder ? ( <> - diff --git a/webapp/src/components/Bid/Bid.types.ts b/webapp/src/components/Bid/Bid.types.ts index ed0dfe9c77..03f7b9a507 100644 --- a/webapp/src/components/Bid/Bid.types.ts +++ b/webapp/src/components/Bid/Bid.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { Bid } from '@dcl/schemas' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' @@ -19,7 +18,6 @@ export type Props = { archivedBidIds: string[] isArchivable?: boolean hasImage?: boolean - onUpdate: (bid: Bid) => void onCancel: typeof cancelBidRequest onArchive: typeof archiveBid onUnarchive: typeof unarchiveBid @@ -28,9 +26,7 @@ export type Props = { } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch< - CallHistoryMethodAction | CancelBidRequestAction | ArchiveBidAction | UnarchiveBidAction | AcceptBidRequestAction -> +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch export type OwnProps = Pick diff --git a/webapp/src/components/BidPage/BidPage.container.ts b/webapp/src/components/BidPage/BidPage.container.ts index 33de735793..54aa34f94b 100644 --- a/webapp/src/components/BidPage/BidPage.container.ts +++ b/webapp/src/components/BidPage/BidPage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { placeBidRequest, PLACE_BID_REQUEST, clearBidError } from '../../modules/bid/actions' import { getLoading } from '../../modules/bid/selectors' @@ -15,7 +14,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)), onPlaceBid: (nft, price, expiresAt, fingerprint) => dispatch(placeBidRequest(nft, price, expiresAt, fingerprint)), onClearBidError: () => dispatch(clearBidError()) }) diff --git a/webapp/src/components/BidPage/BidPage.tsx b/webapp/src/components/BidPage/BidPage.tsx index 37cda369c6..29648adf2a 100644 --- a/webapp/src/components/BidPage/BidPage.tsx +++ b/webapp/src/components/BidPage/BidPage.tsx @@ -1,4 +1,5 @@ -import React from 'react' +import React, { useCallback } from 'react' +import { useHistory } from 'react-router-dom' import { Page } from 'decentraland-ui' import { AssetType } from '../../modules/asset/types' import { AssetProviderPage } from '../AssetProviderPage' @@ -8,7 +9,16 @@ import { BidModal } from './BidModal' import { Props } from './BidPage.types' const BidPage = (props: Props) => { - const { onNavigate, onPlaceBid, isPlacingBid, onClearBidError, getContract } = props + const { onPlaceBid, isPlacingBid, onClearBidError, getContract } = props + const history = useHistory() + + const onNavigate = useCallback( + (pathname: string) => { + history.push(pathname) + }, + [history] + ) + return ( diff --git a/webapp/src/components/BidPage/BidPage.types.ts b/webapp/src/components/BidPage/BidPage.types.ts index cc8afea9d0..e0f8eee677 100644 --- a/webapp/src/components/BidPage/BidPage.types.ts +++ b/webapp/src/components/BidPage/BidPage.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { Authorization } from 'decentraland-dapps/dist/modules/authorization/types' import { clearBidError, ClearBidErrorAction, placeBidRequest, PlaceBidRequestAction } from '../../modules/bid/actions' @@ -9,11 +8,10 @@ export type Props = { authorizations: Authorization[] isPlacingBid: boolean onPlaceBid: typeof placeBidRequest - onNavigate: (path: string) => void getContract: (query: Partial) => ReturnType onClearBidError: typeof clearBidError } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/Bids/Bids.container.ts b/webapp/src/components/Bids/Bids.container.ts index c7b18c830b..0bf4ad13eb 100644 --- a/webapp/src/components/Bids/Bids.container.ts +++ b/webapp/src/components/Bids/Bids.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { replace } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' import { fetchBidsByAddressRequest, FETCH_BIDS_BY_ADDRESS_REQUEST } from '../../modules/bid/actions' @@ -22,7 +21,6 @@ const mapState = (state: RootState): MapStateProps => { } const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(replace(path)), onFetchBids: address => dispatch(fetchBidsByAddressRequest(address)) }) diff --git a/webapp/src/components/Bids/Bids.tsx b/webapp/src/components/Bids/Bids.tsx index d73bd0fa4a..f6e913138a 100644 --- a/webapp/src/components/Bids/Bids.tsx +++ b/webapp/src/components/Bids/Bids.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useCallback } from 'react' -import { useLocation } from 'react-router-dom' +import { useHistory, useLocation } from 'react-router-dom' import { t } from 'decentraland-dapps/dist/modules/translation/utils' import { Loader, HeaderMenu, Header, Button } from 'decentraland-ui' import { locations } from '../../modules/routing/locations' @@ -8,8 +8,8 @@ import { Props } from './Bids.types' import './Bids.css' const Bids = (props: Props) => { - const { wallet, isConnecting, isLoading, bidderBids, sellerBids, archivedBidIds, onNavigate, onFetchBids } = props - + const { wallet, isConnecting, isLoading, bidderBids, sellerBids, archivedBidIds, onFetchBids } = props + const history = useHistory() const [showArchived, setShowArchivedSeller] = useState(false) const { pathname, search } = useLocation() @@ -18,9 +18,9 @@ const Bids = (props: Props) => { // Redirect to signIn if trying to access current account without a wallet useEffect(() => { if (!isConnecting && !wallet) { - onNavigate(locations.signIn(`${pathname}${search}`)) + history.replace(locations.signIn(`${pathname}${search}`)) } - }, [isConnecting, wallet, onNavigate, pathname, search]) + }, [isConnecting, wallet, history, pathname, search]) useEffect(() => { if (wallet) { diff --git a/webapp/src/components/Bids/Bids.types.ts b/webapp/src/components/Bids/Bids.types.ts index a3a43130d1..7a6ef254f9 100644 --- a/webapp/src/components/Bids/Bids.types.ts +++ b/webapp/src/components/Bids/Bids.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { Bid } from '@dcl/schemas' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' @@ -11,10 +10,9 @@ export type Props = { archivedBidIds: string[] isConnecting: boolean isLoading: boolean - onNavigate: (path: string) => void onFetchBids: typeof fetchBidsByAddressRequest } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/CancelSalePage/CancelSalePage.container.ts b/webapp/src/components/CancelSalePage/CancelSalePage.container.ts index 983bccf5af..34ea7d2c1a 100644 --- a/webapp/src/components/CancelSalePage/CancelSalePage.container.ts +++ b/webapp/src/components/CancelSalePage/CancelSalePage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { goBack, push } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { cancelOrderRequest, CANCEL_ORDER_REQUEST } from '../../modules/order/actions' import { getLoading } from '../../modules/order/selectors' @@ -12,8 +11,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)), - onGoBack: () => dispatch(goBack()), onCancelOrder: (order, nft) => dispatch(cancelOrderRequest(order, nft)) }) diff --git a/webapp/src/components/CancelSalePage/CancelSalePage.tsx b/webapp/src/components/CancelSalePage/CancelSalePage.tsx index 09a1013fd6..3ec83c207e 100644 --- a/webapp/src/components/CancelSalePage/CancelSalePage.tsx +++ b/webapp/src/components/CancelSalePage/CancelSalePage.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { useHistory } from 'react-router-dom' import { ChainButton } from 'decentraland-dapps/dist/containers' import { t, T } from 'decentraland-dapps/dist/modules/translation/utils' import { Page, Header, Button } from 'decentraland-ui' @@ -15,7 +16,8 @@ import { Props } from './CancelSalePage.types' import './CancelSalePage.css' const CancelSalePage = (props: Props) => { - const { isLoading, onNavigate, onCancelOrder } = props + const { isLoading, onCancelOrder } = props + const history = useHistory() return ( @@ -53,7 +55,7 @@ const CancelSalePage = (props: Props) => {
{t('cancel_sale_page.title')}
{subtitle}
- + void - onGoBack: (path: string) => void } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/HomePage/HomePage.container.ts b/webapp/src/components/HomePage/HomePage.container.ts index 722d7c6f68..482eb4c3b4 100644 --- a/webapp/src/components/HomePage/HomePage.container.ts +++ b/webapp/src/components/HomePage/HomePage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { getIsCampaignHomepageBannerEnabled } from '../../modules/features/selectors' import { RootState } from '../../modules/reducer' import { fetchAssetsFromRoute } from '../../modules/routing/actions' @@ -14,7 +13,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)), onFetchAssetsFromRoute: options => dispatch(fetchAssetsFromRoute(options)) }) diff --git a/webapp/src/components/HomePage/HomePage.tsx b/webapp/src/components/HomePage/HomePage.tsx index 0d7070c98e..894ea35a60 100644 --- a/webapp/src/components/HomePage/HomePage.tsx +++ b/webapp/src/components/HomePage/HomePage.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useMemo } from 'react' +import { useHistory } from 'react-router-dom' import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils' import { t } from 'decentraland-dapps/dist/modules/translation/utils' import { BackToTopButton, Page } from 'decentraland-ui' @@ -24,8 +25,8 @@ import { Props } from './HomePage.types' import './HomePage.css' const HomePage = (props: Props) => { - const { homepage, homepageLoading, onNavigate, onFetchAssetsFromRoute, isCampaignHomepageBannerEnabled } = props - + const { homepage, homepageLoading, onFetchAssetsFromRoute, isCampaignHomepageBannerEnabled } = props + const history = useHistory() const vendor = VendorName.DECENTRALAND const sections: Partial> = useMemo( @@ -94,7 +95,7 @@ const HomePage = (props: Props) => { let browseOptions: BrowseOptions = {} if (Section.LAND === section) { - onNavigate(locations.lands()) + history.push(locations.lands()) } else if (Section.WEARABLES_TRENDING === section) { trackMessage = 'Explore all trending wearables' browseOptions = { @@ -112,10 +113,10 @@ const HomePage = (props: Props) => { if (trackMessage && browseOptions) { getAnalytics().track(fromEmptyState ? `${trackMessage} '(from empty state)'` : trackMessage) - onNavigate(locations.browse(browseOptions)) + history.push(locations.browse(browseOptions)) } }, - [assetTypes, sort, sections, onNavigate] + [assetTypes, sort, sections, history] ) const fetchAssetsForView = useCallback( diff --git a/webapp/src/components/HomePage/HomePage.types.ts b/webapp/src/components/HomePage/HomePage.types.ts index 5bfe220a78..773ea1f7eb 100644 --- a/webapp/src/components/HomePage/HomePage.types.ts +++ b/webapp/src/components/HomePage/HomePage.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { fetchAssetsFromRoute, FetchAssetsFromRouteAction } from '../../modules/routing/actions' import { getHomepage, getHomepageLoading } from '../../modules/ui/asset/homepage/selectors' @@ -7,10 +6,9 @@ export type Props = { homepage: ReturnType homepageLoading: ReturnType isCampaignHomepageBannerEnabled: boolean - onNavigate: (path: string) => void onFetchAssetsFromRoute: typeof fetchAssetsFromRoute } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/ListPage/ListPage.container.ts b/webapp/src/components/ListPage/ListPage.container.ts index 35d225cc75..c340a1d9eb 100644 --- a/webapp/src/components/ListPage/ListPage.container.ts +++ b/webapp/src/components/ListPage/ListPage.container.ts @@ -1,12 +1,10 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' import { GET_LIST_REQUEST, deleteListStart, getListRequest } from '../../modules/favorites/actions' import { getError, getList, getLoading } from '../../modules/favorites/selectors' import { RootState } from '../../modules/reducer' -import { locations } from '../../modules/routing/locations' import { getWallet } from '../../modules/wallet/selectors' import ListPage from './ListPage' import { MapStateProps, MapDispatch, MapDispatchProps, OwnProps } from './ListPage.types' @@ -25,7 +23,6 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => { } const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onBack: () => dispatch(push(locations.lists())), onFetchList: listId => dispatch(getListRequest(listId)), onEditList: list => dispatch(openModal('CreateOrEditListModal', { list })), onShareList: list => dispatch(openModal('ShareListModal', { list })), diff --git a/webapp/src/components/ListPage/ListPage.spec.tsx b/webapp/src/components/ListPage/ListPage.spec.tsx index 5bc3dbba58..a96bac90d8 100644 --- a/webapp/src/components/ListPage/ListPage.spec.tsx +++ b/webapp/src/components/ListPage/ListPage.spec.tsx @@ -46,7 +46,6 @@ function renderListPage(props: Partial = {}) { wallet={{ address: list.userAddress } as Wallet} listId={listId} list={list} - onBack={jest.fn()} onFetchList={jest.fn()} onDeleteList={jest.fn()} onEditList={jest.fn()} diff --git a/webapp/src/components/ListPage/ListPage.tsx b/webapp/src/components/ListPage/ListPage.tsx index 3587c768b1..e463f3d1ee 100644 --- a/webapp/src/components/ListPage/ListPage.tsx +++ b/webapp/src/components/ListPage/ListPage.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react' -import { Link, Redirect, useLocation } from 'react-router-dom' +import { Link, Redirect, useHistory, useLocation } from 'react-router-dom' import classNames from 'classnames' import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils' import { t } from 'decentraland-dapps/dist/modules/translation/utils' @@ -38,9 +38,10 @@ import styles from './ListPage.module.css' const LIST_NOT_FOUND = 'list was not found' const ListPage = (props: Props) => { - const { isConnecting, wallet, listId, list, isLoading, error, onFetchList, onBack, onEditList, onDeleteList, onShareList } = props + const { isConnecting, wallet, listId, list, isLoading, error, onFetchList, onEditList, onDeleteList, onShareList } = props const hasFetchedOnce = useRef(false) const { pathname, search } = useLocation() + const history = useHistory() const fetchList = useCallback(() => { if (listId && !isLoading && !hasFetchedOnce.current) { @@ -113,7 +114,7 @@ const ListPage = (props: Props) => {
{!isPublicView || list.id === DEFAULT_FAVORITES_LIST_ID ? ( - + history.push(locations.lists())} /> ) : null}
diff --git a/webapp/src/components/ListPage/ListPage.types.ts b/webapp/src/components/ListPage/ListPage.types.ts index a3845cd727..9608412f51 100644 --- a/webapp/src/components/ListPage/ListPage.types.ts +++ b/webapp/src/components/ListPage/ListPage.types.ts @@ -1,11 +1,9 @@ import { RouteComponentProps } from 'react-router-dom' -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { OpenModalAction, openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' import { DeleteListStartAction, GetListRequestAction, deleteListStart, getListRequest } from '../../modules/favorites/actions' import { List } from '../../modules/favorites/types' -import { GoBackAction } from '../../modules/routing/actions' type Params = { listId?: string } @@ -17,7 +15,6 @@ export type Props = { isLoading: boolean error: string | null onFetchList: typeof getListRequest - onBack: () => void onEditList: (list: List) => ReturnType onDeleteList: typeof deleteListStart onShareList?: (list: List) => ReturnType @@ -25,6 +22,6 @@ export type Props = { export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch export type OwnProps = RouteComponentProps diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx index e7abaf0c59..b337bcf5a7 100644 --- a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx +++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx @@ -1,11 +1,7 @@ import { connect } from 'react-redux' -import { push, replace } from 'connected-react-router' import { openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' import { RootState } from '../../../modules/reducer' -import { locations } from '../../../modules/routing/locations' -import { BrowseOptions } from '../../../modules/routing/types' -import { Section } from '../../../modules/vendor/decentraland' import { getWallet } from '../../../modules/wallet/selectors' import ClaimNamePage from './ClaimNamePage' import { MapDispatch, MapDispatchProps, MapStateProps } from './ClaimNamePage.types' @@ -16,9 +12,7 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onBrowse: (options?: BrowseOptions) => dispatch(push(locations.names({ ...options, section: Section.ENS }))), - onClaim: (name: string) => dispatch(openModal('ClaimNameFatFingerModal', { name })), - onRedirect: path => dispatch(replace(path)) + onClaim: (name: string) => dispatch(openModal('ClaimNameFatFingerModal', { name })) }) export default connect(mapState, mapDispatch)(ClaimNamePage) diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx index 3a1743cb9b..1a022aa232 100644 --- a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx +++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx @@ -17,14 +17,10 @@ jest.mock( describe('ClaimNamePage', () => { let walletMock: Wallet - let onBrowseMock: Props['onBrowse'] let onClaimMock: Props['onClaim'] - let onRedirectMock: Props['onRedirect'] const renderAndTypeText = async (text: string) => { - const matchers = renderWithProviders( - - ) + const matchers = renderWithProviders() const { getByDisplayValue, getByText } = matchers const nameInput = getByDisplayValue(t('names_page.your_name')) as HTMLInputElement fireEvent.change(nameInput, { target: { value: text } }) diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx index 949b1b4a9e..cdfd2db2ce 100644 --- a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx +++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useLocation } from 'react-router-dom' +import { useHistory, useLocation } from 'react-router-dom' import classNames from 'classnames' import { isErrorWithMessage } from 'decentraland-dapps/dist/lib' import { t } from 'decentraland-dapps/dist/modules/translation/utils' @@ -39,13 +39,14 @@ const PLACEHOLDER_WIDTH = '94px' const ClaimNamePage = (props: Props) => { const PLACEHOLDER_NAME = t('names_page.your_name') - const { wallet, isConnecting, onClaim, onBrowse, onRedirect } = props + const { wallet, isConnecting, onClaim } = props const location = useLocation() const [isLoadingStatus, setIsLoadingStatus] = useState(false) const [bannedNames, setBannedNames] = useState() const [isAvailable, setIsAvailable] = useState(undefined) const debounceRef = useRef | null>(null) const isMobileOrTable = useTabletAndBelowMediaQuery() + const history = useHistory() useEffect(() => { void (async () => { @@ -109,7 +110,7 @@ const ClaimNamePage = (props: Props) => { const handleClaim = useCallback(() => { if (!isConnecting && !wallet) { - onRedirect(locations.signIn(`${location.pathname}`)) + history.replace(locations.signIn(`${location.pathname}`)) } else { const isValid = isNameValid(name) @@ -117,7 +118,7 @@ const ClaimNamePage = (props: Props) => { onClaim(name) } - }, [isConnecting, wallet, onRedirect, location.pathname, name, isAvailable, onClaim]) + }, [isConnecting, wallet, history, location.pathname, name, isAvailable, onClaim]) const inputRef = useRef(null) @@ -391,7 +392,7 @@ const ClaimNamePage = (props: Props) => {

{t('names_page.ctas.name_taken.title')}

{t('names_page.ctas.name_taken.description')} - +
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts index f32833db49..85c5c2d3b3 100644 --- a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts +++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts @@ -1,17 +1,13 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { OpenModalAction, openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' -import { BrowseOptions } from '../../../modules/routing/types' export type Props = { wallet: Wallet | null isConnecting: boolean - onBrowse: (options?: BrowseOptions) => void onClaim: typeof openModal - onRedirect: (path: string) => void } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/Navbar/Navbar.container.ts b/webapp/src/components/Navbar/Navbar.container.ts index 23f9d35c37..86bd1ee89a 100644 --- a/webapp/src/components/Navbar/Navbar.container.ts +++ b/webapp/src/components/Navbar/Navbar.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push, getLocation } from 'connected-react-router' import { TransactionStatus } from 'decentraland-dapps/dist/modules/transaction/types' import { isPending } from 'decentraland-dapps/dist/modules/transaction/utils' import { getIsChainSelectorEnabled } from '../../modules/features/selectors' @@ -7,17 +6,12 @@ import { getCurrentIdentity } from '../../modules/identity/selectors' import { RootState } from '../../modules/reducer' import { getTransactions } from '../../modules/transaction/selectors' import Navbar from './Navbar' -import { MapStateProps, MapDispatch, MapDispatchProps } from './Navbar.types' +import { MapStateProps } from './Navbar.types' const mapState = (state: RootState): MapStateProps => ({ - location: getLocation(state), hasPendingTransactions: getTransactions(state).some((tx: { status: TransactionStatus | null }) => isPending(tx.status)), identity: getCurrentIdentity(state) || undefined, isChainSelectorEnabled: getIsChainSelectorEnabled(state) }) -const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)) -}) - -export default connect(mapState, mapDispatch)(Navbar) +export default connect(mapState)(Navbar) diff --git a/webapp/src/components/Navbar/Navbar.tsx b/webapp/src/components/Navbar/Navbar.tsx index 8e9e2a4521..ecb04a0af0 100644 --- a/webapp/src/components/Navbar/Navbar.tsx +++ b/webapp/src/components/Navbar/Navbar.tsx @@ -1,4 +1,5 @@ import React, { useCallback } from 'react' +import { useHistory, useLocation } from 'react-router-dom' import { Navbar as BaseNavbar } from 'decentraland-dapps/dist/containers' import { NavbarPages } from 'decentraland-ui/dist/components/Navbar/Navbar.types' import { config } from '../../config' @@ -7,8 +8,9 @@ import { Props } from './Navbar.types' import './Navbar.css' const Navbar = (props: Props) => { - const { location, onNavigate, isChainSelectorEnabled } = props - const { pathname, search } = location + const { isChainSelectorEnabled } = props + const { pathname, search } = useLocation() + const history = useHistory() const handleOnSignIn = useCallback(() => { const searchParams = new URLSearchParams(search) @@ -20,8 +22,8 @@ const Navbar = (props: Props) => { }, [pathname, search]) const handleOnClickAccount = useCallback(() => { - onNavigate(locations.settings()) - }, [onNavigate]) + history.push(locations.settings()) + }, [history]) return ( & { - location: RouterLocation hasPendingTransactions: boolean enablePartialSupportAlert?: boolean - onNavigate: (path: string) => void identity?: AuthIdentity isChainSelectorEnabled: boolean } export type OwnProps = Pick -export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapStateProps = Pick diff --git a/webapp/src/components/SellPage/SellPage.container.ts b/webapp/src/components/SellPage/SellPage.container.ts index 55cb3c5fc4..71ec4a2203 100644 --- a/webapp/src/components/SellPage/SellPage.container.ts +++ b/webapp/src/components/SellPage/SellPage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { goBack } from 'connected-react-router' import { FETCH_AUTHORIZATIONS_REQUEST } from 'decentraland-dapps/dist/modules/authorization/actions' import { getLoading as getLoadingAuthorizations } from 'decentraland-dapps/dist/modules/authorization/selectors' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' @@ -18,7 +17,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onGoBack: () => dispatch(goBack()), onCreateOrder: (nft, price, expiresAt) => dispatch(createOrderRequest(nft, price, expiresAt)), onClearOrderErrors: () => dispatch(clearOrderErrors()) }) diff --git a/webapp/src/components/SellPage/SellPage.tsx b/webapp/src/components/SellPage/SellPage.tsx index 182d6c3025..b572c65b66 100644 --- a/webapp/src/components/SellPage/SellPage.tsx +++ b/webapp/src/components/SellPage/SellPage.tsx @@ -1,4 +1,5 @@ -import React from 'react' +import React, { useCallback } from 'react' +import { useHistory } from 'react-router-dom' import { Page } from 'decentraland-ui' import { AssetType } from '../../modules/asset/types' import { AssetProviderPage } from '../AssetProviderPage' @@ -9,7 +10,11 @@ import { Props } from './SellPage.types' import './SellPage.css' const SellPage = (props: Props) => { - const { isLoading, isCreatingOrder, onGoBack, getContract, onCreateOrder, onClearOrderErrors } = props + const { isLoading, isCreatingOrder, getContract, onCreateOrder, onClearOrderErrors } = props + const history = useHistory() + const onGoBack = useCallback(() => { + history.goBack() + }, [history]) return ( diff --git a/webapp/src/components/SellPage/SellPage.types.ts b/webapp/src/components/SellPage/SellPage.types.ts index 6e5681d4f2..5fd43e651a 100644 --- a/webapp/src/components/SellPage/SellPage.types.ts +++ b/webapp/src/components/SellPage/SellPage.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { getContract } from '../../modules/contract/selectors' import { clearOrderErrors, ClearOrderErrorsAction, createOrderRequest, CreateOrderRequestAction } from '../../modules/order/actions' @@ -9,10 +8,9 @@ export type Props = { isCreatingOrder: boolean getContract: (query: Partial) => ReturnType onCreateOrder: typeof createOrderRequest - onGoBack: () => void onClearOrderErrors: typeof clearOrderErrors } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/SettingsPage/SettingsPage.container.ts b/webapp/src/components/SettingsPage/SettingsPage.container.ts index 0aef44013c..8e1ed49776 100644 --- a/webapp/src/components/SettingsPage/SettingsPage.container.ts +++ b/webapp/src/components/SettingsPage/SettingsPage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { FETCH_AUTHORIZATIONS_REQUEST } from 'decentraland-dapps/dist/modules/authorization/actions' import { getData as getAuthorizations, getLoading, getError } from 'decentraland-dapps/dist/modules/authorization/selectors' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' @@ -34,7 +33,6 @@ const mapState = (state: RootState): MapStateProps => { } const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)), onFetchContracts: () => dispatch(fetchContractsRequest()) }) diff --git a/webapp/src/components/SettingsPage/SettingsPage.types.ts b/webapp/src/components/SettingsPage/SettingsPage.types.ts index d364ea78f5..6a6174a2ef 100644 --- a/webapp/src/components/SettingsPage/SettingsPage.types.ts +++ b/webapp/src/components/SettingsPage/SettingsPage.types.ts @@ -1,4 +1,3 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { Authorization } from 'decentraland-dapps/dist/modules/authorization/types' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' @@ -14,7 +13,6 @@ export type Props = { isConnecting: boolean hasFetchedContracts: boolean getContract: (query: Partial) => ReturnType - onNavigate: (path: string) => void onFetchContracts: typeof fetchContractsRequest } @@ -22,5 +20,5 @@ export type MapStateProps = Pick< Props, 'wallet' | 'authorizations' | 'isLoading' | 'isConnecting' | 'hasError' | 'getContract' | 'hasFetchedContracts' > -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/TransferPage/TransferPage.container.ts b/webapp/src/components/TransferPage/TransferPage.container.ts index 9cffab07a7..8e2a703a9d 100644 --- a/webapp/src/components/TransferPage/TransferPage.container.ts +++ b/webapp/src/components/TransferPage/TransferPage.container.ts @@ -1,5 +1,4 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { transferNFTRequest, TRANSFER_NFT_REQUEST } from '../../modules/nft/actions' import { getLoading } from '../../modules/nft/selectors' @@ -12,7 +11,6 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)), onTransfer: (nft, address) => dispatch(transferNFTRequest(nft, address)) }) diff --git a/webapp/src/components/TransferPage/TransferPage.tsx b/webapp/src/components/TransferPage/TransferPage.tsx index 229b55d326..1eb12906a7 100644 --- a/webapp/src/components/TransferPage/TransferPage.tsx +++ b/webapp/src/components/TransferPage/TransferPage.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useState } from 'react' +import { useHistory } from 'react-router-dom' import { AddressField } from 'decentraland-dapps/dist/components/AddressField' import { ChainButton } from 'decentraland-dapps/dist/containers' import { t, T } from 'decentraland-dapps/dist/modules/translation/utils' @@ -15,7 +16,8 @@ import { Props } from './TransferPage.types' import './TransferPage.css' const TransferPage = (props: Props) => { - const { onNavigate, onTransfer, isTransferring } = props + const { onTransfer, isTransferring } = props + const history = useHistory() const [address, setAddress] = useState('') const [isInvalidAddress, setIsInvalidAddress] = useState(false) @@ -79,7 +81,7 @@ const TransferPage = (props: Props) => { diff --git a/webapp/src/components/TransferPage/TransferPage.types.ts b/webapp/src/components/TransferPage/TransferPage.types.ts index f235a6c5fb..36dac86c60 100644 --- a/webapp/src/components/TransferPage/TransferPage.types.ts +++ b/webapp/src/components/TransferPage/TransferPage.types.ts @@ -1,13 +1,11 @@ -import { CallHistoryMethodAction } from 'connected-react-router' import { Dispatch } from 'redux' import { transferNFTRequest, TransferNFTRequestAction } from '../../modules/nft/actions' export type Props = { onTransfer: typeof transferNFTRequest isTransferring: boolean - onNavigate: (path: string) => void } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch +export type MapDispatchProps = Pick +export type MapDispatch = Dispatch diff --git a/webapp/src/components/Wallet/Wallet.container.ts b/webapp/src/components/Wallet/Wallet.container.ts index 80a753144e..f9c07c287a 100644 --- a/webapp/src/components/Wallet/Wallet.container.ts +++ b/webapp/src/components/Wallet/Wallet.container.ts @@ -1,10 +1,9 @@ import { connect } from 'react-redux' -import { push } from 'connected-react-router' import { isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' import { RootState } from '../../modules/reducer' import { getWallet } from '../../modules/wallet/selectors' import Wallet from './Wallet' -import { MapStateProps, MapDispatchProps, MapDispatch } from './Wallet.types' +import { MapStateProps } from './Wallet.types' const mapState = (state: RootState): MapStateProps => { return { @@ -13,8 +12,4 @@ const mapState = (state: RootState): MapStateProps => { } } -const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onNavigate: path => dispatch(push(path)) -}) - -export default connect(mapState, mapDispatch)(Wallet) +export default connect(mapState)(Wallet) diff --git a/webapp/src/components/Wallet/Wallet.types.ts b/webapp/src/components/Wallet/Wallet.types.ts index 876975e3b6..dba9ae1ec7 100644 --- a/webapp/src/components/Wallet/Wallet.types.ts +++ b/webapp/src/components/Wallet/Wallet.types.ts @@ -1,15 +1,10 @@ import React from 'react' -import { CallHistoryMethodAction } from 'connected-react-router' -import { Dispatch } from 'redux' import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types' export type Props = { wallet: Wallet | null isLoading: boolean - onNavigate: (path: string) => void children: (wallet: Wallet) => React.ReactNode } export type MapStateProps = Pick -export type MapDispatchProps = Pick -export type MapDispatch = Dispatch diff --git a/webapp/src/modules/asset/sagas.spec.ts b/webapp/src/modules/asset/sagas.spec.ts index 61cbd2a1f5..357dfaad9a 100644 --- a/webapp/src/modules/asset/sagas.spec.ts +++ b/webapp/src/modules/asset/sagas.spec.ts @@ -1,6 +1,5 @@ import util from 'util' -import { getLocation, push } from 'connected-react-router' -import { call, select } from 'redux-saga/effects' +import { call, getContext } from 'redux-saga/effects' import { expectSaga } from 'redux-saga-test-plan' import { Network } from '@dcl/schemas' import { setPurchase } from 'decentraland-dapps/dist/modules/gateway/actions' @@ -55,49 +54,40 @@ describe('when handling the set purchase action', () => { describe('when an NFT has been purchased and it is in status pending', () => { describe('and the user still waiting for the purchase in the same page', () => { it('should dispatch a push to the history with the location of the buy status page', () => { + const pushMock = jest.fn() return expectSaga(assetSaga) - .provide([ - [ - select(getLocation), - { - pathname: mockPathname() - } - ] - ]) - .put(push(locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId))) + .provide([[getContext('history'), { location: { pathname: mockPathname() }, push: pushMock }]]) .dispatch(setPurchase(mockNFTPurchase)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId)) + }) }) }) describe('and the user was redirected to the processing page and the purchase changed it status', () => { it('should dispatch a push to the history with the location of the buy status page', () => { + const pushMock = jest.fn() return expectSaga(assetSaga) .provide([ [ - select(getLocation), - { - pathname: locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId) - } + getContext('history'), + { location: { pathname: locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId) }, push: pushMock } ] ]) - .put(push(locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId))) .dispatch(setPurchase(mockNFTPurchase)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.buyStatusPage(AssetType.ITEM, mockContractAddress, mockTokenId)) + }) }) }) describe('and the user was exploring collectibles', () => { it('should not dispatch a push to the history with the location of the buy status page', () => { + const pushMock = jest.fn() return expectSaga(assetSaga) - .provide([ - [ - select(getLocation), - { - pathname: locations.browse() - } - ] - ]) + .provide([[getContext('history'), { location: { pathname: locations.browse() }, push: pushMock }]]) .dispatch(setPurchase(mockNFTPurchase)) .run({ silenceTimeout: true }) .then(({ effects }) => { @@ -109,14 +99,8 @@ describe('when handling the set purchase action', () => { describe('and the tx hash has not yet been setted', () => { it('should not dispatch a push to the history with the location of the buy status page', () => { return expectSaga(assetSaga) - .provide([ - [ - select(getLocation), - { - pathname: locations.browse() - } - ] - ]) + .provide([[getContext('history'), { location: { pathname: locations.browse() } }]]) + .dispatch(setPurchase({ ...mockNFTPurchase, txHash: null })) .run({ silenceTimeout: true }) .then(({ effects }) => { @@ -130,36 +114,26 @@ describe('when handling the set purchase action', () => { 'when the purchase of an item has a status %s', (status: PurchaseStatus) => { it('should dispatch an action signaling the failure of the item', () => { + const pushMock = jest.fn() return expectSaga(assetSaga) - .provide([ - [ - select(getLocation), - { - pathname: mockPathname() - } - ] - ]) + .provide([[getContext('history'), { location: { pathname: mockPathname() }, push: pushMock }]]) + .put(buyItemWithCardFailure(t('global.unknown_error'))) - .put(push(locations.buyWithCard(AssetType.ITEM, mockContractAddress, mockTokenId))) .dispatch(setPurchase({ ...mockNFTPurchase, status })) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.buyWithCard(AssetType.ITEM, mockContractAddress, mockTokenId)) + }) }) } ) describe.each(failStatuses)('when the purchase of an nft has a status %s', status => { it('should dispatch an action signaling the failure of the nft', () => { + const pushMock = jest.fn() return expectSaga(assetSaga) - .provide([ - [ - select(getLocation), - { - pathname: mockPathname(AssetType.NFT) - } - ] - ]) + .provide([[getContext('history'), { location: { pathname: mockPathname(AssetType.NFT) }, push: pushMock }]]) .put(executeOrderWithCardFailure(t('global.unknown_error'))) - .put(push(locations.buyWithCard(AssetType.NFT, mockContractAddress, mockTokenId))) .dispatch( setPurchase({ ...mockNFTPurchase, @@ -173,6 +147,9 @@ describe('when handling the set purchase action', () => { }) ) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.buyWithCard(AssetType.NFT, mockContractAddress, mockTokenId)) + }) }) }) }) diff --git a/webapp/src/modules/asset/sagas.ts b/webapp/src/modules/asset/sagas.ts index 2f955fa750..937dfde3f9 100644 --- a/webapp/src/modules/asset/sagas.ts +++ b/webapp/src/modules/asset/sagas.ts @@ -1,5 +1,5 @@ -import { getLocation, push } from 'connected-react-router' -import { call, put, select, takeEvery } from 'redux-saga/effects' +import { History } from 'history' +import { call, getContext, put, takeEvery } from 'redux-saga/effects' import { SetPurchaseAction, SET_PURCHASE } from 'decentraland-dapps/dist/modules/gateway/actions' import { TradeType } from 'decentraland-dapps/dist/modules/gateway/transak/types' import { PurchaseStatus } from 'decentraland-dapps/dist/modules/gateway/types' @@ -32,9 +32,11 @@ export function* assetSaga() { function* handleSetAssetPurchaseWithCard(action: SetPurchaseAction) { const { purchase } = action.payload + const history: History = yield getContext('history') + if (isNFTPurchase(purchase)) { const { nft, status } = purchase - const { pathname } = (yield select(getLocation)) as ReturnType + const { pathname } = history.location const { tradeType, contractAddress, tokenId, itemId } = nft const assetType: AssetType = tradeType === TradeType.PRIMARY ? AssetType.ITEM : AssetType.NFT @@ -44,13 +46,13 @@ function* handleSetAssetPurchaseWithCard(action: SetPurchaseAction) { const shouldRedirect = [new URL(`${window.origin}${buyWithCardPathname}`).pathname, statusPagePathname].includes(pathname) if (shouldRedirect && [PurchaseStatus.PENDING, PurchaseStatus.COMPLETE].includes(status)) { - yield put(push(statusPagePathname)) + history.push(statusPagePathname) } if (failStatuses.includes(status)) { const failureAction = assetType === AssetType.NFT ? executeOrderWithCardFailure : buyItemWithCardFailure - if (shouldRedirect) yield put(push(buyWithCardPathname)) + if (shouldRedirect) history.push(buyWithCardPathname) // TODO (buy nfts with card): is there a way to get the reason of the failure? yield put(failureAction(t('global.unknown_error'))) diff --git a/webapp/src/modules/favorites/sagas.spec.ts b/webapp/src/modules/favorites/sagas.spec.ts index 8839492b74..90063e76b3 100644 --- a/webapp/src/modules/favorites/sagas.spec.ts +++ b/webapp/src/modules/favorites/sagas.spec.ts @@ -1,5 +1,4 @@ -import { getLocation, push } from 'connected-react-router' -import { call, put, select, take } from 'redux-saga/effects' +import { call, getContext, put, select, take } from 'redux-saga/effects' import { expectSaga } from 'redux-saga-test-plan' import * as matchers from 'redux-saga-test-plan/matchers' import { throwError } from 'redux-saga-test-plan/providers' @@ -58,6 +57,7 @@ import { convertListsBrowseSortByIntoApiSortBy } from './utils' let item: Item let address: string let error: Error +let pushMock: jest.Mock const getIdentity = () => undefined @@ -65,6 +65,7 @@ beforeEach(() => { error = new Error('error') item = { id: 'anAddress-itemId', itemId: 'itemId' } as Item address = '0xb549b2442b2bd0a53795bc5cdcbfe0caf7aca9f8' + pushMock = jest.fn() }) describe('when handling the request for fetching favorited items', () => { @@ -872,7 +873,7 @@ describe('when handling the request for deleting a list', () => { .provide([ [call(getAccountIdentity), Promise.resolve()], [matchers.call.fn(FavoritesAPI.prototype.deleteList), Promise.resolve()], - [select(getLocation), { pathname: locations.lists() }] + [getContext('history'), { location: { pathname: locations.lists() } }] ]) .call.like({ fn: FavoritesAPI.prototype.deleteList, @@ -894,20 +895,24 @@ describe('when handling the success deletion of a list', () => { describe('and the user performed the action from the list detail page', () => { it('should dispatch an action to redirect the user to the My Lists tab', () => { return expectSaga(favoritesSaga, getIdentity) - .provide([[select(getLocation), { pathname: locations.list(list.id) }]]) - .put(push(locations.lists())) + .provide([[getContext('history'), { location: { pathname: locations.list(list.id) }, push: pushMock }]]) .dispatch(deleteListSuccess(list)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.lists()) + }) }) }) describe('and the user performed the action from the My Lists tab', () => { it('should not dispatch the action signaling the redirection', () => { return expectSaga(favoritesSaga, getIdentity) - .provide([[select(getLocation), { pathname: locations.lists() }]]) - .not.put(push(locations.lists())) + .provide([[getContext('history'), { location: { pathname: locations.lists(), push: pushMock } }]]) .dispatch(deleteListSuccess(list)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).not.toHaveBeenCalled() + }) }) }) }) @@ -1104,7 +1109,7 @@ describe('when handling the request for creating a list', () => { it('should dispatch an action signaling the failure of the handled action', () => { return expectSaga(favoritesSaga, getIdentity) .provide([ - [select(getLocation), { pathname: locations.lists() }], + [getContext('history'), { location: { pathname: locations.lists() } }], [call(getAccountIdentity), Promise.reject(error)] ]) .put(createListFailure(error.message)) @@ -1117,7 +1122,7 @@ describe('when handling the request for creating a list', () => { it('should dispatch an action signaling the failure of the handled action', () => { return expectSaga(favoritesSaga, getIdentity) .provide([ - [select(getLocation), { pathname: locations.lists() }], + [getContext('history'), { location: { pathname: locations.lists() } }], [call(getAccountIdentity), Promise.resolve()], [matchers.call.fn(FavoritesAPI.prototype.createList), Promise.reject(error)] ]) @@ -1136,7 +1141,7 @@ describe('when handling the request for creating a list', () => { it('should dispatch an action signaling the success of the handled action and a push to the created list page', () => { return expectSaga(favoritesSaga, getIdentity) .provide([ - [select(getLocation), { pathname: locations.lists() }], + [getContext('history'), { location: { pathname: locations.lists() }, push: pushMock }], [call(getAccountIdentity), Promise.resolve()], [matchers.call.fn(FavoritesAPI.prototype.createList), Promise.resolve(returnedList)] ]) @@ -1145,9 +1150,11 @@ describe('when handling the request for creating a list', () => { args: [listToCreate] }) .put(createListSuccess(returnedList)) - .put(push(locations.list(returnedList.id))) .dispatch(createListRequest(listToCreate)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.list(returnedList.id)) + }) }) }) @@ -1155,7 +1162,7 @@ describe('when handling the request for creating a list', () => { it('should dispatch an action signaling the success of the handled action without a push to the created list page', () => { return expectSaga(favoritesSaga, getIdentity) .provide([ - [select(getLocation), { pathname: locations.browse() }], + [getContext('history'), { location: { pathname: locations.browse() } }], [call(getAccountIdentity), Promise.resolve()], [matchers.call.fn(FavoritesAPI.prototype.createList), Promise.resolve(returnedList)] ]) @@ -1164,9 +1171,11 @@ describe('when handling the request for creating a list', () => { args: [listToCreate] }) .put(createListSuccess(returnedList)) - .not.put(push(locations.list(returnedList.id))) .dispatch(createListRequest(listToCreate)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).not.toHaveBeenCalled() + }) }) }) }) diff --git a/webapp/src/modules/favorites/sagas.ts b/webapp/src/modules/favorites/sagas.ts index 997adb511d..34b897909e 100644 --- a/webapp/src/modules/favorites/sagas.ts +++ b/webapp/src/modules/favorites/sagas.ts @@ -1,5 +1,5 @@ -import { getLocation, push } from 'connected-react-router' -import { call, put, race, select, take, takeEvery } from 'redux-saga/effects' +import { History } from 'history' +import { call, getContext, put, race, select, take, takeEvery } from 'redux-saga/effects' import { CatalogFilters, Item } from '@dcl/schemas' import { closeModal, CloseModalAction, CLOSE_MODAL, openModal } from 'decentraland-dapps/dist/modules/modal/actions' import { ConnectWalletSuccessAction, CONNECT_WALLET_FAILURE, CONNECT_WALLET_SUCCESS } from 'decentraland-dapps/dist/modules/wallet/actions' @@ -200,8 +200,9 @@ export function* favoritesSaga(getIdentity: () => AuthIdentity | undefined) { } function* handleDeleteListSuccess() { - const { pathname } = (yield select(getLocation)) as ReturnType - if (pathname !== locations.lists()) yield put(push(locations.lists())) + const history: History = yield getContext('history') + const { pathname } = history.location + if (pathname !== locations.lists()) history.push(locations.lists()) } function* handleDeleteListRequest(action: DeleteListRequestAction) { @@ -246,8 +247,9 @@ export function* favoritesSaga(getIdentity: () => AuthIdentity | undefined) { function* handleCreateListRequest(action: CreateListRequestAction) { const { name, isPrivate, description } = action.payload + const history: History = yield getContext('history') try { - const { pathname } = (yield select(getLocation)) as ReturnType + const { pathname } = history.location // Force the user to have the signed identity yield call(getAccountIdentity) const list = (yield call([favoritesAPI, 'createList'], { @@ -257,7 +259,7 @@ export function* favoritesSaga(getIdentity: () => AuthIdentity | undefined) { })) as Awaited> yield put(createListSuccess(list)) if (pathname === locations.lists()) { - yield put(push(locations.list(list.id))) + history.push(locations.list(list.id)) } } catch (error) { yield put(createListFailure(isErrorWithMessage(error) ? error.message : 'Unknown error')) diff --git a/webapp/src/modules/item/sagas.spec.ts b/webapp/src/modules/item/sagas.spec.ts index 52a31856b4..be6b6ea75b 100644 --- a/webapp/src/modules/item/sagas.spec.ts +++ b/webapp/src/modules/item/sagas.spec.ts @@ -1,5 +1,4 @@ -import { getLocation } from 'connected-react-router' -import { call, select, take } from 'redux-saga/effects' +import { call, getContext, select, take } from 'redux-saga/effects' import { expectSaga } from 'redux-saga-test-plan' import * as matchers from 'redux-saga-test-plan/matchers' import { ChainId, Entity, EntityType, Item, Network, Rarity } from '@dcl/schemas' @@ -407,7 +406,7 @@ describe('when handling the fetch items request action', () => { [matchers.call.fn(waitForFeatureFlagsToBeLoaded), undefined], [select(getWallet), wallet], [select(getIsMarketplaceServerEnabled), true], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname } }], { call(effect, next) { if (effect.fn === CatalogAPI.prototype.get && effect.args[0] === originalBrowseOptions.filters) { @@ -443,7 +442,7 @@ describe('when handling the fetch items request action', () => { [matchers.call.fn(waitForFeatureFlagsToBeLoaded), undefined], [select(getWallet), wallet], [select(getIsMarketplaceServerEnabled), true], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname } }], { call(effect, next) { if (effect.fn === CatalogAPI.prototype.get && effect.args[0] === originalBrowseOptions.filters) { @@ -484,7 +483,7 @@ describe('when handling the fetch items request action', () => { [matchers.call.fn(waitForWalletConnectionAndIdentityIfConnecting), undefined], [matchers.call.fn(waitForFeatureFlagsToBeLoaded), undefined], [select(getWallet), undefined], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname } }], [select(getIsMarketplaceServerEnabled), false] ]) .put(fetchItemsSuccess(fetchResult.data, fetchResult.total, itemBrowseOptions, nowTimestamp)) @@ -498,7 +497,7 @@ describe('when handling the fetch items request action', () => { it('should dispatch a failing action with the error and the options', () => { return expectSaga(itemSaga, getIdentity) .provide([ - [select(getLocation), { pathname: '' }], + [getContext('history'), { location: { pathname: '' } }], [select(getWallet), undefined], [select(getIsMarketplaceServerEnabled), true], [select(getWallet), undefined], diff --git a/webapp/src/modules/item/sagas.ts b/webapp/src/modules/item/sagas.ts index 8d4532731c..c69d1b7dd6 100644 --- a/webapp/src/modules/item/sagas.ts +++ b/webapp/src/modules/item/sagas.ts @@ -1,9 +1,9 @@ import { matchPath } from 'react-router-dom' import { put, takeEvery } from '@redux-saga/core/effects' -import { getLocation } from 'connected-react-router' import { ethers } from 'ethers' +import { History } from 'history' import { SagaIterator, Task } from 'redux-saga' -import { call, cancel, cancelled, fork, race, select, take } from 'redux-saga/effects' +import { call, cancel, cancelled, fork, race, select, take, getContext } from 'redux-saga/effects' import { Entity } from '@dcl/schemas' import { getConnectedProvider } from 'decentraland-dapps/dist/lib/eth' import { SetPurchaseAction, SET_PURCHASE } from 'decentraland-dapps/dist/modules/gateway/actions' @@ -94,10 +94,11 @@ export function* itemSaga(getIdentity: () => AuthIdentity | undefined) { // to avoid race conditions, just one fetch items request is handled at once in the browse page function* takeLatestByPath(actionType: string, path: string): SagaIterator { let task: Task | undefined + const history: History = yield getContext('history') while (true) { const action: FetchItemsRequestAction = yield take(actionType) - const { pathname: currentPathname }: ReturnType = yield select(getLocation) + const { pathname: currentPathname } = history.location // if we have a task running in the browse path, we cancel the previous one if (matchPath(currentPathname, { path }) && task && task.isRunning()) { diff --git a/webapp/src/modules/routing/sagas.spec.ts b/webapp/src/modules/routing/sagas.spec.ts index 898b359658..0e0b05d016 100644 --- a/webapp/src/modules/routing/sagas.spec.ts +++ b/webapp/src/modules/routing/sagas.spec.ts @@ -1,6 +1,6 @@ -import { getLocation, LOCATION_CHANGE, LocationChangeAction, push, RouterLocation } from 'connected-react-router' +import { LOCATION_CHANGE, LocationChangeAction, RouterLocation } from 'connected-react-router' import { BigNumber, ethers } from 'ethers' -import { call, select } from 'redux-saga/effects' +import { call, getContext, select } from 'redux-saga/effects' import { expectSaga } from 'redux-saga-test-plan' import { ChainId, @@ -40,8 +40,11 @@ import { getCurrentBrowseOptions, getLatestVisitedLocation, getSection } from '. import { BrowseOptions, SortBy } from './types' import { buildBrowseURL } from './utils' +let pushMock: jest.Mock + beforeEach(() => { jest.spyOn(Date, 'now').mockReturnValue(100) + pushMock = jest.fn() }) afterEach(() => { @@ -95,13 +98,15 @@ describe('when handling the clear filters request action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getEventData), {}], [call(fetchAssetsFromRoute, browseOptionsWithoutFilters), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, browseOptionsWithoutFilters))) .dispatch(clearFilters()) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, browseOptionsWithoutFilters)) + }) }) }) @@ -110,13 +115,15 @@ describe('when handling the clear filters request action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getEventData), {}], [call(fetchAssetsFromRoute, browseOptionsWithoutFilters), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, browseOptionsWithoutFilters))) .dispatch(clearFilters()) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, browseOptionsWithoutFilters)) + }) }) describe('and it is not the LAND section', () => { @@ -134,12 +141,14 @@ describe('when handling the clear filters request action', () => { } ], [select(getPage), 1], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getEventData), {}], [call(fetchAssetsFromRoute, browseOptionsWithoutFilters), Promise.resolve()] ]) - .put( - push( + .dispatch(clearFilters()) + .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith( buildBrowseURL(pathname, { ...browseOptionsWithoutFilters, section: Section.COLLECTIONS, @@ -147,9 +156,7 @@ describe('when handling the clear filters request action', () => { status: AssetStatusFilter.ON_SALE }) ) - ) - .dispatch(clearFilters()) - .run({ silenceTimeout: true }) + }) }) }) describe('and the asset type is NFT', () => { @@ -167,12 +174,14 @@ describe('when handling the clear filters request action', () => { } ], [select(getPage), 1], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getEventData), {}], [call(fetchAssetsFromRoute, browseOptionsWithoutFilters), Promise.resolve()] ]) - .put( - push( + .dispatch(clearFilters()) + .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith( buildBrowseURL(pathname, { ...browseOptionsWithoutFilters, section: Section.WEARABLES, @@ -180,9 +189,7 @@ describe('when handling the clear filters request action', () => { assetType: AssetType.NFT }) ) - ) - .dispatch(clearFilters()) - .run({ silenceTimeout: true }) + }) }) }) }) @@ -440,14 +447,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -465,14 +474,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -490,14 +501,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -515,14 +528,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -551,14 +566,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -576,14 +593,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -607,14 +626,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -631,14 +652,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -655,14 +678,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -680,14 +705,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -716,14 +743,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -741,14 +770,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -766,14 +797,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -791,14 +824,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -827,14 +862,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -852,14 +889,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -883,14 +922,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -907,14 +948,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) @@ -931,14 +974,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -956,14 +1001,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -989,14 +1036,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), eventContracts], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, browseOptions))) .dispatch(browse(browseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, browseOptions)) + }) }) }) @@ -1013,14 +1062,16 @@ describe('when handling the browse action', () => { return expectSaga(routingSaga) .provide([ [select(getCurrentBrowseOptions), browseOptions], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getSection), Section.WEARABLES], [select(getEventData), eventContracts], [call(fetchAssetsFromRoute, browseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, browseOptions))) .dispatch(browse(browseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, browseOptions)) + }) }) }) @@ -1049,13 +1100,15 @@ describe('when handling the browse action', () => { .provide([ [select(getCurrentBrowseOptions), {}], [select(getSection), Section.WEARABLES], - [select(getLocation), { pathname }], + [getContext('history'), { location: { pathname }, push: pushMock }], [select(getEventData), {}], [call(fetchAssetsFromRoute, expectedBrowseOptions), Promise.resolve()] ]) - .put(push(buildBrowseURL(pathname, expectedBrowseOptions))) .dispatch(browse(newBrowseOptions)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(buildBrowseURL(pathname, expectedBrowseOptions)) + }) }) }) }) @@ -1184,7 +1237,7 @@ describe('handleRedirectToSuccessPage saga', () => { it('should redirect to success page with the correct query params', () => { return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) + .provide([[getContext('history'), { push: pushMock }]]) .dispatch( executeOrderSuccess(searchParams.txHash, { tokenId: searchParams.tokenId, @@ -1192,6 +1245,9 @@ describe('handleRedirectToSuccessPage saga', () => { } as NFT) ) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) @@ -1207,7 +1263,7 @@ describe('handleRedirectToSuccessPage saga', () => { it('should redirect to success page with the correct query params', () => { return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) + .provide([[getContext('history'), { push: pushMock }]]) .dispatch( buyItemSuccess(ChainId.ETHEREUM_GOERLI, searchParams.txHash, { itemId: searchParams.tokenId, @@ -1216,6 +1272,9 @@ describe('handleRedirectToSuccessPage saga', () => { } as Item) ) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) @@ -1237,9 +1296,12 @@ describe('handleRedirectToSuccessPage saga', () => { it('should redirect to success page with the correct query params', () => { return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) + .provide([[getContext('history'), { push: pushMock }]]) .dispatch(claimNameSuccess(ens, 'aName', searchParams.txHash)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) @@ -1283,9 +1345,12 @@ describe('handleRedirectToSuccessPage saga', () => { it('should redirect to success page with the correct query params', () => { return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) + .provide([[getContext('history'), { push: pushMock }]]) .dispatch(buyItemCrossChainSuccess(route, ChainId.ETHEREUM_MAINNET, searchParams.txHash, item, order)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) @@ -1301,9 +1366,12 @@ describe('handleRedirectToSuccessPage saga', () => { it('should redirect to success page with the correct query params', () => { return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) + .provide([[getContext('history'), { push: pushMock }]]) .dispatch(buyItemCrossChainSuccess(route, ChainId.ETHEREUM_MAINNET, searchParams.txHash, item)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) }) @@ -1316,26 +1384,32 @@ describe.each([ it('should redirect to the location when redirectTo is present', () => { const redirectTo = '/account?section=on_sale' const location = { search: `?redirectTo=${encodeURIComponent(redirectTo)}` } + const pushMock = jest.fn() return ( expectSaga(routingSaga) - .provide([[select(getLocation), location]]) + .provide([[getContext('history'), { location, push: pushMock }]]) //@ts-ignore .dispatch(action(...args)) - .put(push(redirectTo)) .run() + .then(() => { + expect(pushMock).toHaveBeenCalledWith(redirectTo) + }) ) }) it('should redirect to the default activity location when redirectTo is not present', () => { const location = { search: '' } + const pushMock = jest.fn() return ( expectSaga(routingSaga) - .provide([[select(getLocation), location]]) + .provide([[getContext('history'), { location, push: pushMock }]]) //@ts-ignore .dispatch(action(...args)) - .put(push(locations.activity())) .run() + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.activity()) + }) ) }) }) @@ -1371,9 +1445,10 @@ describe('when handling the claim name transaction submitted action', () => { }) it('should redirect to success page with the correct query params', () => { + const pushMock = jest.fn() return expectSaga(routingSaga) - .put(push(locations.success(searchParams))) .provide([ + [getContext('history'), { push: pushMock }], [call(getSigner), {}], [ call([DCLRegistrar__factory, 'connect'], REGISTRAR_ADDRESS, signer), @@ -1385,5 +1460,8 @@ describe('when handling the claim name transaction submitted action', () => { ]) .dispatch(claimNameTransactionSubmitted(subdomain, address, chainId, txHash)) .run({ silenceTimeout: true }) + .then(() => { + expect(pushMock).toHaveBeenCalledWith(locations.success(searchParams)) + }) }) }) diff --git a/webapp/src/modules/routing/sagas.ts b/webapp/src/modules/routing/sagas.ts index b313ce1c8b..0fa77b46ec 100644 --- a/webapp/src/modules/routing/sagas.ts +++ b/webapp/src/modules/routing/sagas.ts @@ -1,6 +1,7 @@ import { matchPath } from 'react-router-dom' -import { push, getLocation, goBack, LOCATION_CHANGE, replace, LocationChangeAction } from 'connected-react-router' -import { takeEvery, put, select, call, take, delay, race, spawn } from 'redux-saga/effects' +import { getLocation, LOCATION_CHANGE, LocationChangeAction } from 'connected-react-router' +import { History } from 'history' +import { takeEvery, put, select, call, take, delay, race, spawn, getContext } from 'redux-saga/effects' import { CatalogFilters, CatalogSortBy, NFTCategory, RentalStatus, Sale, SaleSortBy, SaleType } from '@dcl/schemas' import { getSigner } from 'decentraland-dapps/dist/lib/eth' import { TRANSACTION_ACTION_FLAG } from 'decentraland-dapps/dist/modules/transaction/types' @@ -124,17 +125,19 @@ function* handleFetchAssetsFromRoute(action: FetchAssetsFromRouteAction) { } function* handleClearFilters() { + const history: History = yield getContext('history') const browseOptions = (yield select(getCurrentBrowseOptions)) as ReturnType - const { pathname } = (yield select(getLocation)) as ReturnType + const { pathname } = history.location const clearedBrowseOptions = getClearedBrowseOptions(browseOptions) yield call(fetchAssetsFromRoute, clearedBrowseOptions) - yield put(push(buildBrowseURL(pathname, clearedBrowseOptions))) + history.push(buildBrowseURL(pathname, clearedBrowseOptions)) } export function* handleBrowse(action: BrowseAction) { + const history: History = yield getContext('history') const options = (yield call(getNewBrowseOptions, action.payload.options)) as BrowseOptions - const { pathname } = (yield select(getLocation)) as ReturnType + const { pathname } = history.location const eventsContracts = (yield select(getData)) as Record const isAnEventRoute = Object.keys(eventsContracts).includes(pathname.slice(1)) yield call(fetchAssetsFromRoute, { @@ -143,13 +146,14 @@ export function* handleBrowse(action: BrowseAction) { contracts: options.contracts && options.contracts.length > 0 ? options.contracts : eventsContracts[pathname.slice(1)] }) }) - yield put(push(buildBrowseURL(pathname, options))) + history.push(buildBrowseURL(pathname, options)) } function* handleGoBack(action: GoBackAction) { + const history: History = yield getContext('history') const { defaultLocation } = action.payload - yield put(goBack()) + history.goBack() const { timeout }: { timeout?: boolean } = (yield race({ changed: take(LOCATION_CHANGE), @@ -157,7 +161,7 @@ function* handleGoBack(action: GoBackAction) { })) as { changed: LocationChangeAction; timeout: boolean } if (timeout) { - yield put(replace(defaultLocation || locations.root())) + history.replace(defaultLocation || locations.root()) } } @@ -560,32 +564,32 @@ function shouldResetOptions(previous: BrowseOptions, current: BrowseOptions) { } function* handleRedirectToActivity() { - const location = (yield select(getLocation)) as ReturnType + const history: History = yield getContext('history') + const location = history.location const redirectTo = new URLSearchParams(location.search).get('redirectTo') if (redirectTo) { - yield put(push(decodeURIComponent(redirectTo))) + history.push(decodeURIComponent(redirectTo)) } else { - yield put(push(locations.activity())) + history.push(locations.activity()) } } function* handleRedirectClaimingNameToSuccessPage(action: ClaimNameTransactionSubmittedAction) { + const history: History = yield getContext('history') const data = action.payload[TRANSACTION_ACTION_FLAG] as { hash: string; payload: { subdomain: string } } const signer = (yield call(getSigner)) as Awaited> const dclRegistrarContract: DCLRegistrar = (yield call([DCLRegistrar__factory, 'connect'], REGISTRAR_ADDRESS, signer)) as Awaited< ReturnType > const contractAddress = dclRegistrarContract.address - yield put( - push( - locations.success({ - txHash: data.hash, - assetType: AssetType.NFT, - tokenId: '', - contractAddress, - subdomain: data.payload.subdomain - }) - ) + history.push( + locations.success({ + txHash: data.hash, + assetType: AssetType.NFT, + tokenId: '', + contractAddress, + subdomain: data.payload.subdomain + }) ) } @@ -597,6 +601,7 @@ function* handleRedirectToSuccessPage( | ClaimNameSuccessAction | ClaimNameCrossChainSuccessAction ) { + const history: History = yield getContext('history') const payload = action.payload const isCrossChainAction = 'route' in payload && payload.route.route.params.fromChain !== payload.route.route.params.toChain // it's cross chain only if the fromChain is different from the toChain const successParams = { @@ -627,5 +632,5 @@ function* handleRedirectToSuccessPage( ? payload.ens.contractAddress : '' } - yield put(push(locations.success(successParams))) + history.push(locations.success(successParams)) } diff --git a/webapp/src/modules/store.ts b/webapp/src/modules/store.ts index e3e8877236..124cc05e11 100644 --- a/webapp/src/modules/store.ts +++ b/webapp/src/modules/store.ts @@ -40,7 +40,7 @@ export function initStore(history: History) { const rootReducer = storageReducerWrapper(createRootReducer(history)) - const sagasMiddleware = createSagasMiddleware() + const sagasMiddleware = createSagasMiddleware({ context: { history } }) const loggerMiddleware = createLogger({ collapsed: () => true, predicate: (_: any, action: Action) => isDev || action.type.includes('Failure') diff --git a/webapp/src/modules/ui/sagas.ts b/webapp/src/modules/ui/sagas.ts index e9a0338b2f..850573856d 100644 --- a/webapp/src/modules/ui/sagas.ts +++ b/webapp/src/modules/ui/sagas.ts @@ -1,6 +1,6 @@ -import { push, getLocation } from 'connected-react-router' +import { History } from 'history' import { eventChannel } from 'redux-saga' -import { takeEvery, put, select, take, spawn } from 'redux-saga/effects' +import { takeEvery, put, take, spawn, getContext } from 'redux-saga/effects' import { IPreviewController, PreviewEmoteEventType } from '@dcl/schemas' import { CONNECT_WALLET_SUCCESS, ConnectWalletSuccessAction } from 'decentraland-dapps/dist/modules/wallet/actions' import { locations } from '../routing/locations' @@ -14,14 +14,15 @@ export function* uiSaga() { } function* handleConnectWalletSuccess(_action: ConnectWalletSuccessAction) { - const location = (yield select(getLocation)) as ReturnType + const history: History = yield getContext('history') + const location = history.location const { pathname, search } = location if (pathname === locations.signIn()) { const redirectTo = new URLSearchParams(search).get('redirectTo') if (redirectTo) { - yield put(push(decodeURIComponent(redirectTo))) + history.push(decodeURIComponent(redirectTo)) } else { - yield put(push(locations.defaultCurrentAccount())) + history.push(locations.defaultCurrentAccount()) } } }