diff --git a/packages/yoroi-extension/app/Routes.js b/packages/yoroi-extension/app/Routes.js index f174240a40..6f0100904e 100644 --- a/packages/yoroi-extension/app/Routes.js +++ b/packages/yoroi-extension/app/Routes.js @@ -1,28 +1,24 @@ // @flow -import React, { Suspense } from 'react'; import type { Node } from 'react'; -import { Route, Redirect, Switch } from 'react-router-dom'; -import { ROUTES } from './routes-config'; import type { StoresMap } from './stores/index'; import type { ActionsMap } from './actions/index'; import type { InjectedOrGenerated } from './types/injectedPropsType'; import type { GeneratedData as SettingsData } from './containers/settings/Settings'; +import type { GeneratedData as SwapData } from './containers/swap/SwapPageContainer'; import type { GeneratedData as WalletData } from './containers/wallet/Wallet'; import type { GeneratedData as ReceiveData } from './containers/wallet/Receive'; import type { ConfigType } from '../config/config-types'; import type { GeneratedData as AssetsData } from './containers/wallet/AssetsWrapper'; -import LoadingPage from './containers/LoadingPage'; +import { Route, Redirect, Switch } from 'react-router-dom'; +import { ROUTES } from './routes-config'; +import React, { Suspense } from 'react'; import StakingPage, { StakingPageContentPromise } from './containers/wallet/staking/StakingPage'; -import Wallet from './containers/wallet/Wallet'; -import Settings from './containers/settings/Settings'; import Transfer, { WalletTransferPagePromise } from './containers/transfer/Transfer'; import VotingPage, { VotingPageContentPromise } from './containers/wallet/voting/VotingPage'; import ConnectedWebsitesPage, { ConnectedWebsitesPagePromise, } from './containers/dapp-connector/ConnectedWebsitesContainer'; import AddWalletPage, { AddAnotherWalletPromise } from './containers/wallet/AddWalletPage'; -import AssetsWrapper from './containers/wallet/AssetsWrapper'; -import NFTsWrapper from './containers/wallet/NFTsWrapper'; // Todo: Add lazy loading import RestoreWalletPage, { RestoreWalletPagePromise, @@ -30,6 +26,12 @@ import RestoreWalletPage, { import CreateWalletPage, { CreateWalletPagePromise, } from './containers/wallet/CreateWalletPageContainer'; +import LoadingPage from './containers/LoadingPage'; +import Wallet from './containers/wallet/Wallet'; +import Settings from './containers/settings/Settings'; +import SwapPageContainer from './containers/swap/SwapPageContainer'; +import AssetsWrapper from './containers/wallet/AssetsWrapper'; +import NFTsWrapper from './containers/wallet/NFTsWrapper'; // PAGES const LanguageSelectionPagePromise = () => import('./containers/profile/LanguageSelectionPage'); @@ -129,6 +131,12 @@ const YoroiPalettePage = React.lazy(YoroiPalettePagePromise); const YoroiThemesPagePromise = () => import('./containers/experimental/yoroiThemes'); const YoroiThemesPage = React.lazy(YoroiThemesPagePromise); +// SWAP +const SwapPagePromise = () => import('./containers/swap/asset-swap/SwapPage'); +const SwapPage = React.lazy(SwapPagePromise); +const SwapOrdersPagePromise = () => import('./containers/swap/orders/OrdersPage'); +const SwapOrdersPage = React.lazy(SwapOrdersPagePromise); + export const LazyLoadPromises: Array<() => any> = [ AddAnotherWalletPromise, StakingPageContentPromise, @@ -167,6 +175,8 @@ export const LazyLoadPromises: Array<() => any> = [ ConnectedWebsitesPagePromise, YoroiPalettePagePromise, YoroiThemesPagePromise, + SwapPagePromise, + SwapOrdersPagePromise, OptForAnalyticsPagePromise, AnalyticsSettingsPagePromise, ]; @@ -275,6 +285,10 @@ export const Routes = (stores: StoresMap, actions: ActionsMap): Node => ( wrapSettings({ ...props, stores, actions }, SettingsSubpages(stores, actions)) } /> + wrapSwap({ ...props, stores, actions }, SwapSubpages(stores, actions))} + /> } @@ -364,6 +378,22 @@ const WalletsSubpages = (stores, actions) => ( ); +const SwapSubpages = (stores, actions) => ( + + } + /> + } + /> + + +); + const SettingsSubpages = (stores, actions) => ( ( ( - - )} + component={props => } /> @@ -446,6 +474,14 @@ const NFTsSubPages = (stores, actions) => ( ); +export function wrapSwap(swapProps: InjectedOrGenerated, children: Node): Node { + return ( + + {children} + + ); +} + export function wrapSettings( settingsProps: InjectedOrGenerated, children: Node diff --git a/packages/yoroi-extension/app/assets/images/sidebar/revamp/swap.inline.svg b/packages/yoroi-extension/app/assets/images/sidebar/revamp/swap.inline.svg index 8ccb8bc703..19b397d373 100644 --- a/packages/yoroi-extension/app/assets/images/sidebar/revamp/swap.inline.svg +++ b/packages/yoroi-extension/app/assets/images/sidebar/revamp/swap.inline.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/packages/yoroi-extension/app/components/swap/SwapMenu.js b/packages/yoroi-extension/app/components/swap/SwapMenu.js new file mode 100644 index 0000000000..db82b17489 --- /dev/null +++ b/packages/yoroi-extension/app/components/swap/SwapMenu.js @@ -0,0 +1,52 @@ +// @flow +import type { Node } from 'react'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import { Component } from 'react'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import { ROUTES } from '../../routes-config'; +import SubMenu from '../topbar/SubMenu'; + +const messages = defineMessages({ + assetSwapLabel: { + id: 'swap.menu.swap', + defaultMessage: '!!!Asset swap', + }, + orderSwapLabel: { + id: 'swap.menu.orders', + defaultMessage: '!!!Orders', + }, +}); + +type Props = {| + +isActiveItem: string => boolean, + +onItemClick: string => void, +|}; +@observer +export default class SwapMenu extends Component { + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { + intl: intlShape.isRequired, + }; + + render(): Node { + const { intl } = this.context; + const { onItemClick, isActiveItem } = this.props; + + const settingOptions: Array = [ + { + label: intl.formatMessage(messages.assetSwapLabel), + route: ROUTES.SWAP.ROOT, + className: 'swap', + }, + { + label: intl.formatMessage(messages.orderSwapLabel), + route: ROUTES.SWAP.ORDERS, + className: 'orders', + }, + ]; + + return ( + + ); + } +} diff --git a/packages/yoroi-extension/app/containers/swap/SwapPageContainer.js b/packages/yoroi-extension/app/containers/swap/SwapPageContainer.js new file mode 100644 index 0000000000..faf6c9579c --- /dev/null +++ b/packages/yoroi-extension/app/containers/swap/SwapPageContainer.js @@ -0,0 +1,134 @@ +// @flow +import type { Node, ComponentType } from 'react'; +import type { GeneratedData as BannerContainerData } from '../banners/BannerContainer'; +import type { InjectedOrGenerated } from '../../types/injectedPropsType'; +import type { GeneratedData as SidebarContainerData } from '../SidebarContainer'; +import type { GeneratedData as NavBarContainerRevampData } from '../NavBarContainerRevamp'; +import type { $npm$ReactIntl$IntlFormat } from 'react-intl'; +import type { LayoutComponentMap } from '../../styles/context/layout'; +import { Component } from 'react'; +import { computed } from 'mobx'; +import { observer } from 'mobx-react'; +import { intlShape } from 'react-intl'; +import { buildRoute } from '../../utils/routing'; +import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver/index'; +import { withLayout } from '../../styles/context/layout'; +import globalMessages from '../../i18n/global-messages'; +import SwapMenu from '../../components/swap/SwapMenu'; +import BannerContainer from '../banners/BannerContainer'; +import TopBarLayout from '../../components/layout/TopBarLayout'; +import SidebarContainer from '../SidebarContainer'; +import NavBarTitle from '../../components/topbar/NavBarTitle'; +import NavBarContainerRevamp from '../NavBarContainerRevamp'; + +export type GeneratedData = typeof SwapPageContainer.prototype.generated; + +type Props = {| + ...InjectedOrGenerated, + +children?: Node, +|}; + +type InjectedProps = {| + +renderLayoutComponent: LayoutComponentMap => Node, +|}; + +type AllProps = {| ...Props, ...InjectedProps |}; + +@observer +class SwapPageContainer extends Component { + static defaultProps: {| children: void |} = { + children: undefined, + }; + + static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { + intl: intlShape.isRequired, + }; + + isActivePage: string => boolean = route => { + const { location } = this.generated.stores.router; + if (location) { + return location.pathname === buildRoute(route); + } + return false; + }; + + render(): Node { + const { children } = this.props; + const { actions } = this.generated; + const sidebarContainer = ; + + const menu = ( + actions.router.goToRoute.trigger({ route })} + isActiveItem={this.isActivePage} + /> + ); + + return ( + } + sidebar={sidebarContainer} + navbar={ + + } + menu={menu} + /> + } + showInContainer + showAsCard + > + {children} + + ); + } + + @computed get generated(): {| + BannerContainerProps: InjectedOrGenerated, + NavBarContainerRevampProps: InjectedOrGenerated, + SidebarContainerProps: InjectedOrGenerated, + actions: {| + router: {| + goToRoute: {| + trigger: (params: {| + publicDeriver?: null | PublicDeriver<>, + params?: ?any, + route: string, + |}) => void, + |}, + |}, + |}, + stores: {| + router: {| location: any |}, + wallets: {| selected: null | PublicDeriver<> |}, + |}, + |} { + if (this.props.generated !== undefined) { + return this.props.generated; + } + if (this.props.stores == null || this.props.actions == null) { + throw new Error(`${nameof(SwapPageContainer)} no way to generated props`); + } + const { stores, actions } = this.props; + return Object.freeze({ + stores: { + router: { location: stores.router.location }, + wallets: { selected: stores.wallets.selected }, + }, + actions: { + router: { + goToRoute: { trigger: actions.router.goToRoute.trigger }, + }, + }, + SidebarContainerProps: ({ actions, stores }: InjectedOrGenerated), + NavBarContainerRevampProps: ({ + actions, + stores, + }: InjectedOrGenerated), + BannerContainerProps: ({ actions, stores }: InjectedOrGenerated), + }); + } +} +export default (withLayout(SwapPageContainer): ComponentType); diff --git a/packages/yoroi-extension/app/containers/swap/asset-swap/SwapPage.js b/packages/yoroi-extension/app/containers/swap/asset-swap/SwapPage.js new file mode 100644 index 0000000000..29ee9a8a03 --- /dev/null +++ b/packages/yoroi-extension/app/containers/swap/asset-swap/SwapPage.js @@ -0,0 +1,6 @@ +// @flow +import type { Node } from 'react'; + +export default function SwapPage(): Node { + return <>Swap form here; +} diff --git a/packages/yoroi-extension/app/containers/swap/orders/OrdersPage.js b/packages/yoroi-extension/app/containers/swap/orders/OrdersPage.js new file mode 100644 index 0000000000..ce4857bd6d --- /dev/null +++ b/packages/yoroi-extension/app/containers/swap/orders/OrdersPage.js @@ -0,0 +1,6 @@ +// @flow +import type { Node } from 'react'; + +export default function SwapOrdersPage(): Node { + return <>Orders here coming soon; +} diff --git a/packages/yoroi-extension/app/i18n/locales/en-US.json b/packages/yoroi-extension/app/i18n/locales/en-US.json index ff4e808fa2..6727e1ebde 100644 --- a/packages/yoroi-extension/app/i18n/locales/en-US.json +++ b/packages/yoroi-extension/app/i18n/locales/en-US.json @@ -324,6 +324,8 @@ "settings.menu.termsOfService.link.label": "Terms of Service Agreement", "settings.menu.termsOfUse.link.label": "Terms of use", "settings.menu.wallet.link.label": "Wallet", + "swap.menu.swap": "Asset swap", + "swap.menu.orders": "Orders", "settings.noexternal.dialog.content": "Your memos are stored locally. They will not automatically sync with other Yoroi instances and will be lost if you delete Yoroi", "settings.noexternal.dialog.title": "No external storage", "settings.paperWallet.createPaper.label": "Create Paper Wallet", diff --git a/packages/yoroi-extension/app/routes-config.js b/packages/yoroi-extension/app/routes-config.js index 76359f5f41..82fee1d127 100644 --- a/packages/yoroi-extension/app/routes-config.js +++ b/packages/yoroi-extension/app/routes-config.js @@ -62,19 +62,23 @@ export const ROUTES = { }, NFTS: { ROOT: '/nfts', - DETAILS: '/nfts/:nftId' + DETAILS: '/nfts/:nftId', }, DAPP_CONNECTOR: { - CONNECTED_WEBSITES: '/connector/connected-websites' + CONNECTED_WEBSITES: '/connector/connected-websites', }, EXPERIMENTAL: { YOROI_PALETTE: '/experimental/yoroi-palette', YOROI_COMPONENTS: '/experimental/components', - THEMES: '/experimental/themes' + THEMES: '/experimental/themes', }, // Revamp specific routes: REVAMP: { // `voting` is part of the sidebar CATALYST_VOTING: '/voting', - } + }, + SWAP: { + ROOT: '/swap', + ORDERS: '/swap/orders', + }, }; diff --git a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js index 28af077d92..73b70abd55 100644 --- a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js +++ b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js @@ -1,26 +1,25 @@ // @flow -import { ROUTES } from '../../routes-config'; import type { MessageDescriptor } from 'react-intl'; +import { ROUTES } from '../../routes-config'; import globalMessages, { connectorMessages } from '../../i18n/global-messages'; -import { ReactComponent as walletsIcon } from '../../assets/images/sidebar/my_wallets.inline.svg'; -import { ReactComponent as transferIcon } from '../../assets/images/sidebar/transfer_wallets.inline.svg'; -import { ReactComponent as settingsIcon } from '../../assets/images/sidebar/wallet-settings-2-ic.inline.svg'; -import { ReactComponent as goBackIcon } from '../../assets/images/top-bar/back-arrow-white.inline.svg'; -import { ReactComponent as dappConnectorIcon } from '../../assets/images/dapp-connector/dapp-connector.inline.svg'; -import { ReactComponent as noticeBoardIcon } from '../../assets/images/notice-board/notice-board.inline.svg'; import { matchRoute } from '../../utils/routing'; -import environment from '../../environment'; import { asGetStakingKey } from '../../api/ada/lib/storage/models/PublicDeriver/traits'; - -import { ReactComponent as walletIcon } from '../../assets/images/sidebar/revamp/wallet.inline.svg'; -import { ReactComponent as stakingIcon } from '../../assets/images/sidebar/revamp/staking.inline.svg'; -import { ReactComponent as assetsIcon } from '../../assets/images/sidebar/revamp/assets.inline.svg'; +import { ReactComponent as walletsIcon } from '../../assets/images/sidebar/my_wallets.inline.svg'; +import { ReactComponent as transferIcon } from '../../assets/images/sidebar/transfer_wallets.inline.svg'; +import { ReactComponent as settingsIcon } from '../../assets/images/sidebar/wallet-settings-2-ic.inline.svg'; +import { ReactComponent as goBackIcon } from '../../assets/images/top-bar/back-arrow-white.inline.svg'; +import { ReactComponent as dappConnectorIcon } from '../../assets/images/dapp-connector/dapp-connector.inline.svg'; +import { ReactComponent as noticeBoardIcon } from '../../assets/images/notice-board/notice-board.inline.svg'; +import { ReactComponent as walletIcon } from '../../assets/images/sidebar/revamp/wallet.inline.svg'; +import { ReactComponent as stakingIcon } from '../../assets/images/sidebar/revamp/staking.inline.svg'; +import { ReactComponent as assetsIcon } from '../../assets/images/sidebar/revamp/assets.inline.svg'; import { ReactComponent as nftsIcon } from '../../assets/images/sidebar/revamp/nfts.inline.svg'; -import { ReactComponent as votingIcon } from '../../assets/images/sidebar/revamp/voting.inline.svg'; -// import { ReactComponent as swapIcon } from '../../assets/images/sidebar/revamp/swap.inline.svg'; -import { ReactComponent as settingIcon } from '../../assets/images/sidebar/revamp/setting.inline.svg'; +import { ReactComponent as votingIcon } from '../../assets/images/sidebar/revamp/voting.inline.svg'; +import { ReactComponent as swapIcon } from '../../assets/images/sidebar/revamp/swap.inline.svg'; +import { ReactComponent as settingIcon } from '../../assets/images/sidebar/revamp/setting.inline.svg'; import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver'; import { isCardanoHaskell } from '../../api/ada/lib/storage/database/prepackaged/networks'; +import environment from '../../environment'; export type SidebarCategory = {| +className: string, @@ -94,7 +93,6 @@ export const TRANSFER_PAGE: SidebarCategory = registerCategory({ isVisible: _request => true, }); - export const CONNECTED_WEBSITES: SidebarCategory = registerCategory({ className: 'dapp-connector', route: ROUTES.DAPP_CONNECTOR.CONNECTED_WEBSITES, @@ -111,11 +109,11 @@ export const NOTICE_BOARD: SidebarCategory = registerCategory({ }); type isVisibleFunc = ({| - hasAnyWallets: boolean, - selected: null | PublicDeriver<>, - currentRoute: string, - isRewardWallet: isRewardWalletFunc, - |}) => boolean; + hasAnyWallets: boolean, + selected: null | PublicDeriver<>, + currentRoute: string, + isRewardWallet: isRewardWalletFunc, +|}) => boolean; type isRewardWalletFunc = (PublicDeriver<>) => boolean; @@ -132,28 +130,31 @@ export const allCategoriesRevamp: Array = [ // Open `/wallets` only if the user is on any other page other than `/wallets/add` makeWalletCategory( ROUTES.WALLETS.ROOT, - ({ currentRoute, hasAnyWallets }) => currentRoute !== ROUTES.WALLETS.ADD && hasAnyWallets, + ({ currentRoute, hasAnyWallets }) => currentRoute !== ROUTES.WALLETS.ADD && hasAnyWallets ), // Open `/wallets/transactions` if the user is on the `/wallet/add` makeWalletCategory( ROUTES.WALLETS.TRANSACTIONS, - ({ currentRoute, hasAnyWallets }) => currentRoute === ROUTES.WALLETS.ADD && hasAnyWallets, + ({ currentRoute, hasAnyWallets }) => currentRoute === ROUTES.WALLETS.ADD && hasAnyWallets ), // If user didn't restored any wallets, it should redirect to the add wallet page. - makeWalletCategory( - ROUTES.WALLETS.ADD, - ({ hasAnyWallets }) => !hasAnyWallets, - ), + makeWalletCategory(ROUTES.WALLETS.ADD, ({ hasAnyWallets }) => !hasAnyWallets), { className: 'staking', route: ROUTES.STAKING, icon: stakingIcon, label: globalMessages.sidebarStaking, - isVisible: ({ selected, isRewardWallet }) => ( + isVisible: ({ selected, isRewardWallet }) => !!selected && isCardanoHaskell(selected.getParent().getNetworkInfo()) && - isRewardWallet(selected) - ), + isRewardWallet(selected), + }, + { + className: 'swap', + route: ROUTES.SWAP.ROOT, + icon: swapIcon, + label: globalMessages.sidebarSwap, + isVisible: () => environment.isDev(), }, { className: 'assets', @@ -221,5 +222,5 @@ function makeWalletCategory(route: string, isVisible: isVisibleFunc): SidebarCat icon: walletIcon, label: globalMessages.walletLabel, isVisible, - } -}; \ No newline at end of file + }; +}