Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(swap): add swap page with tabs #3277

Merged
merged 12 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions packages/yoroi-extension/app/Routes.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
// @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,
} from './containers/wallet/restore/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');
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -167,6 +175,8 @@ export const LazyLoadPromises: Array<() => any> = [
ConnectedWebsitesPagePromise,
YoroiPalettePagePromise,
YoroiThemesPagePromise,
SwapPagePromise,
SwapOrdersPagePromise,
OptForAnalyticsPagePromise,
AnalyticsSettingsPagePromise,
];
Expand Down Expand Up @@ -275,6 +285,10 @@ export const Routes = (stores: StoresMap, actions: ActionsMap): Node => (
wrapSettings({ ...props, stores, actions }, SettingsSubpages(stores, actions))
}
/>
<Route
path={ROUTES.SWAP.ROOT}
component={props => wrapSwap({ ...props, stores, actions }, SwapSubpages(stores, actions))}
/>
<Route
path={ROUTES.TRANSFER.ROOT}
component={props => <Transfer {...props} stores={stores} actions={actions} />}
Expand Down Expand Up @@ -364,6 +378,22 @@ const WalletsSubpages = (stores, actions) => (
</Switch>
);

const SwapSubpages = (stores, actions) => (
<Switch>
<Route
exact
path={ROUTES.SWAP.ROOT}
component={props => <SwapPage {...props} stores={stores} actions={actions} />}
/>
<Route
exact
path={ROUTES.SWAP.ORDERS}
component={props => <SwapOrdersPage {...props} stores={stores} actions={actions} />}
/>
<Redirect to={ROUTES.SWAP.ROOT} />
</Switch>
);

const SettingsSubpages = (stores, actions) => (
<Switch>
<Route
Expand Down Expand Up @@ -408,9 +438,7 @@ const SettingsSubpages = (stores, actions) => (
<Route
exact
path={ROUTES.SETTINGS.ANALYTICS}
component={props => (
<AnalyticsSettingsPage {...props} stores={stores} actions={actions} />
)}
component={props => <AnalyticsSettingsPage {...props} stores={stores} actions={actions} />}
/>
<Redirect to={ROUTES.SETTINGS.GENERAL} />
</Switch>
Expand Down Expand Up @@ -446,6 +474,14 @@ const NFTsSubPages = (stores, actions) => (
</Switch>
);

export function wrapSwap(swapProps: InjectedOrGenerated<SwapData>, children: Node): Node {
return (
<SwapPageContainer {...swapProps}>
<Suspense fallback={null}>{children}</Suspense>
</SwapPageContainer>
);
}

export function wrapSettings(
settingsProps: InjectedOrGenerated<SettingsData>,
children: Node
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions packages/yoroi-extension/app/components/swap/SwapMenu.js
Original file line number Diff line number Diff line change
@@ -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<Props> {
static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = {
intl: intlShape.isRequired,
};

render(): Node {
const { intl } = this.context;
const { onItemClick, isActiveItem } = this.props;

const settingOptions: Array<Object> = [
{
label: intl.formatMessage(messages.assetSwapLabel),
route: ROUTES.SWAP.ROOT,
className: 'swap',
},
{
label: intl.formatMessage(messages.orderSwapLabel),
route: ROUTES.SWAP.ORDERS,
className: 'orders',
},
];

return (
<SubMenu options={settingOptions} onItemClick={onItemClick} isActiveItem={isActiveItem} />
);
}
}
134 changes: 134 additions & 0 deletions packages/yoroi-extension/app/containers/swap/SwapPageContainer.js
Original file line number Diff line number Diff line change
@@ -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<GeneratedData>,
+children?: Node,
|};

type InjectedProps = {|
+renderLayoutComponent: LayoutComponentMap => Node,
|};

type AllProps = {| ...Props, ...InjectedProps |};

@observer
class SwapPageContainer extends Component<AllProps> {
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 = <SidebarContainer {...this.generated.SidebarContainerProps} />;

const menu = (
<SwapMenu
onItemClick={route => actions.router.goToRoute.trigger({ route })}
isActiveItem={this.isActivePage}
/>
);

return (
<TopBarLayout
banner={<BannerContainer {...this.generated.BannerContainerProps} />}
sidebar={sidebarContainer}
navbar={
<NavBarContainerRevamp
{...this.generated.NavBarContainerRevampProps}
title={
<NavBarTitle title={this.context.intl.formatMessage(globalMessages.sidebarSwap)} />
}
menu={menu}
/>
}
showInContainer
showAsCard
>
{children}
</TopBarLayout>
);
}

@computed get generated(): {|
BannerContainerProps: InjectedOrGenerated<BannerContainerData>,
NavBarContainerRevampProps: InjectedOrGenerated<NavBarContainerRevampData>,
SidebarContainerProps: InjectedOrGenerated<SidebarContainerData>,
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<SidebarContainerData>),
NavBarContainerRevampProps: ({
actions,
stores,
}: InjectedOrGenerated<NavBarContainerRevampData>),
BannerContainerProps: ({ actions, stores }: InjectedOrGenerated<BannerContainerData>),
});
}
}
export default (withLayout(SwapPageContainer): ComponentType<Props>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow
import type { Node } from 'react';

export default function SwapPage(): Node {
return <>Swap form here</>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow
import type { Node } from 'react';

export default function SwapOrdersPage(): Node {
return <>Orders here coming soon</>;
}
2 changes: 2 additions & 0 deletions packages/yoroi-extension/app/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 8 additions & 4 deletions packages/yoroi-extension/app/routes-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
};
Loading