From 27fcf2a4f0ed13c420313183201254d64def880c Mon Sep 17 00:00:00 2001 From: SerDorosh Date: Thu, 28 Apr 2022 12:02:35 +0300 Subject: [PATCH] feat: FIFS-174 Wrap/Unwrap --- packages/frontend/src/components/Routing.js | 338 +++++++++++------- .../src/components/svg/CollapseIcon.js | 21 ++ .../frontend/src/components/svg/WrapIcon.js | 33 ++ .../frontend/src/components/wallet/Wallet.js | 223 +++++++----- .../frontend/src/components/wrap/SwapNear.js | 306 ++++++++++++++++ .../components/ReviewTransactionDetails.js | 218 +++++++++++ .../wrap/components/SelectTokenButton.js | 44 +++ .../src/components/wrap/components/Success.js | 69 ++++ .../wrap/components/SwapAmountForm.js | 172 +++++++++ .../wrap/components/SwapFromForm.js | 106 ++++++ .../components/wrap/components/SwapFromTo.js | 104 ++++++ .../wrap/components/SwapReviewForm.js | 138 +++++++ .../wrap/components/TransactionDetails.js | 64 ++++ .../src/components/wrap/components/helper.js | 77 ++++ .../frontend/src/services/FungibleTokens.js | 134 +++++-- .../frontend/src/translations/en.global.json | 21 ++ .../frontend/src/translations/pt.global.json | 21 ++ .../frontend/src/translations/ru.global.json | 21 ++ .../frontend/src/translations/tr.global.json | 21 ++ .../frontend/src/translations/ua.global.json | 142 ++++---- .../frontend/src/translations/vi.global.json | 21 ++ .../src/translations/zh-hans.global.json | 132 ++++--- .../src/translations/zh-hant.global.json | 132 ++++--- 23 files changed, 2142 insertions(+), 416 deletions(-) create mode 100644 packages/frontend/src/components/svg/CollapseIcon.js create mode 100644 packages/frontend/src/components/svg/WrapIcon.js create mode 100644 packages/frontend/src/components/wrap/SwapNear.js create mode 100644 packages/frontend/src/components/wrap/components/ReviewTransactionDetails.js create mode 100644 packages/frontend/src/components/wrap/components/SelectTokenButton.js create mode 100644 packages/frontend/src/components/wrap/components/Success.js create mode 100644 packages/frontend/src/components/wrap/components/SwapAmountForm.js create mode 100644 packages/frontend/src/components/wrap/components/SwapFromForm.js create mode 100644 packages/frontend/src/components/wrap/components/SwapFromTo.js create mode 100644 packages/frontend/src/components/wrap/components/SwapReviewForm.js create mode 100644 packages/frontend/src/components/wrap/components/TransactionDetails.js create mode 100644 packages/frontend/src/components/wrap/components/helper.js diff --git a/packages/frontend/src/components/Routing.js b/packages/frontend/src/components/Routing.js index ffaa28ae4f..59e8c45f0c 100644 --- a/packages/frontend/src/components/Routing.js +++ b/packages/frontend/src/components/Routing.js @@ -10,9 +10,18 @@ import { connect } from 'react-redux'; import { Redirect, Switch } from 'react-router-dom'; import styled, { ThemeProvider } from 'styled-components'; -import { CREATE_IMPLICIT_ACCOUNT, IMPORT_ACCOUNT_WITH_LINK_V2, CREATE_USN_CONTRACT } from '../../../../features'; +import { + CREATE_IMPLICIT_ACCOUNT, + IMPORT_ACCOUNT_WITH_LINK_V2, + CREATE_USN_CONTRACT, +} from '../../../../features'; import TwoFactorVerifyModal from '../components/accounts/two_factor/TwoFactorVerifyModal'; -import { IS_MAINNET, PUBLIC_URL, SHOW_PRERELEASE_WARNING, DISABLE_CREATE_ACCOUNT } from '../config'; +import { + IS_MAINNET, + PUBLIC_URL, + SHOW_PRERELEASE_WARNING, + DISABLE_CREATE_ACCOUNT, +} from '../config'; import ExampleFlag from '../ExampleFlag'; import { Mixpanel } from '../mixpanel/index'; import * as accountActions from '../redux/actions/account'; @@ -44,7 +53,7 @@ import { WALLET_CREATE_NEW_ACCOUNT_FLOW_URLS, WALLET_LOGIN_URL, WALLET_SIGN_URL, - WALLET_SEND_MONEY_URL + WALLET_SEND_MONEY_URL, } from '../utils/wallet'; import AccessKeysWrapper from './access-keys/v2/AccessKeysWrapper'; import { AutoImportWrapper } from './accounts/auto_import/AutoImportWrapper'; @@ -86,13 +95,11 @@ import { SendContainerWrapper } from './send/SendContainerWrapper'; import { StakingContainer } from './staking/StakingContainer'; import SwapContainerWrapper from './Swap/SwapContainerWrapper'; import Terms from './terms/Terms'; +import { SwapNear } from './wrap/SwapNear'; import '../index.css'; -const { - fetchTokenFiatValues, - getTokenWhiteList -} = tokenFiatValueActions; +const { fetchTokenFiatValues, getTokenWhiteList } = tokenFiatValueActions; const { handleClearUrl, @@ -100,7 +107,7 @@ const { handleRefreshUrl, promptTwoFactor, redirectTo, - refreshAccount + refreshAccount, } = accountActions; const { handleFlowLimitation } = flowLimitationActions; @@ -122,14 +129,15 @@ const Container = styled.div` } &.network-banner { @media (max-width: 450px) { - .alert-banner, .lockup-avail-transfer { + .alert-banner, + .lockup-avail-transfer { margin-top: -45px; } } } @media (max-width: 767px) { - &.hide-footer-mobile { + &.hide-footer-mobile { .wallet-footer { display: none; } @@ -155,24 +163,32 @@ class Routing extends Component { ]; const browserLanguage = getBrowserLocale(languages.map((l) => l.code)); - const activeLang = localStorage.getItem('languageCode') || browserLanguage || languages[0].code; + const activeLang = + localStorage.getItem('languageCode') || + browserLanguage || + languages[0].code; this.props.initialize({ languages, options: { defaultLanguage: 'en', - onMissingTranslation: ({ translationId, defaultTranslation }) => { + onMissingTranslation: ({ + translationId, + defaultTranslation, + }) => { if (isString(defaultTranslation)) { // do anything to change the defaultTranslation as you wish return defaultTranslation; } else { // that's the code that can fix the issue - return ReactDOMServer.renderToStaticMarkup(defaultTranslation); + return ReactDOMServer.renderToStaticMarkup( + defaultTranslation + ); } }, renderToStaticMarkup: ReactDOMServer.renderToStaticMarkup, - renderInnerHtml: true - } + renderInnerHtml: true, + }, }); // TODO: Figure out how to load only necessary translations dynamically @@ -199,7 +215,7 @@ class Routing extends Component { router, fetchTokenFiatValues, handleClearAlert, - handleFlowLimitation + handleFlowLimitation, } = this.props; fetchTokenFiatValues(); @@ -210,23 +226,32 @@ class Routing extends Component { history.listen(async () => { handleRedirectUrl(this.props.router.location); handleClearUrl(); - if (!WALLET_CREATE_NEW_ACCOUNT_FLOW_URLS.find((path) => this.props.router.location.pathname.indexOf(path) > -1)) { + if ( + !WALLET_CREATE_NEW_ACCOUNT_FLOW_URLS.find( + (path) => + this.props.router.location.pathname.indexOf(path) > -1 + ) + ) { await refreshAccount(true); } handleClearAlert(); handleFlowLimitation(); }); - } + }; componentDidUpdate(prevProps) { const { activeLanguage, account } = this.props; - if (prevProps.account.accountId !== account.accountId && account.accountId !== undefined) { + if ( + prevProps.account.accountId !== account.accountId && + account.accountId !== undefined + ) { this.props.getTokenWhiteList(account.accountId); } - const prevLangCode = prevProps.activeLanguage && prevProps.activeLanguage.code; + const prevLangCode = + prevProps.activeLanguage && prevProps.activeLanguage.code; const curLangCode = activeLanguage && activeLanguage.code; const hasLanguageChanged = prevLangCode !== curLangCode; @@ -238,27 +263,38 @@ class Routing extends Component { componentWillUnmount = () => { this.stopPollingTokenFiatValue(); - } + }; startPollingTokenFiatValue = () => { const { fetchTokenFiatValues } = this.props; const handlePollTokenFiatValue = async () => { - await fetchTokenFiatValues().catch(() => { }); + await fetchTokenFiatValues().catch(() => {}); if (this.pollTokenFiatValue) { - this.pollTokenFiatValue = setTimeout(() => handlePollTokenFiatValue(), 30000); + this.pollTokenFiatValue = setTimeout( + () => handlePollTokenFiatValue(), + 30000 + ); } }; - this.pollTokenFiatValue = setTimeout(() => handlePollTokenFiatValue(), 30000); - } + this.pollTokenFiatValue = setTimeout( + () => handlePollTokenFiatValue(), + 30000 + ); + }; stopPollingTokenFiatValue = () => { clearTimeout(this.pollTokenFiatValue); this.pollTokenFiatValue = null; - } + }; render() { - const { search, query: { tab }, hash, pathname } = this.props.router.location; + const { + search, + query: { tab }, + hash, + pathname, + } = this.props.router.location; const { account } = this.props; const setTab = (nextTab) => { if (tab !== nextTab) { @@ -276,7 +312,7 @@ class Routing extends Component { const hideFooterOnMobile = [ WALLET_LOGIN_URL, WALLET_SEND_MONEY_URL, - WALLET_SIGN_URL + WALLET_SIGN_URL, ].includes(pathname.replace(/\//g, '')); const accountFound = this.props.account.localStorage?.accountFound; @@ -287,27 +323,31 @@ class Routing extends Component { - + - + - { - account.requestPending !== null && + {account.requestPending !== null && ( { - const { account, promptTwoFactor } = this.props; + const { account, promptTwoFactor } = + this.props; Mixpanel.track('2FA Modal Verify start'); // requestPending will resolve (verified == true) or reject the Promise being awaited in the method that dispatched promptTwoFactor account.requestPending(verified, error); @@ -315,58 +355,75 @@ class Routing extends Component { promptTwoFactor(null); if (error) { // tracking error - Mixpanel.track('2FA Modal Verify fail', { error: error.message }); + Mixpanel.track( + '2FA Modal Verify fail', + { error: error.message } + ); } if (verified) { - Mixpanel.track('2FA Modal Verify finish'); + Mixpanel.track( + '2FA Modal Verify finish' + ); } }} /> - } + )} - + } + path="/" + render={(props) => ( + + )} accountFound={accountFound} indexBySearchEngines={!accountFound} /> - {CREATE_IMPLICIT_ACCOUNT && + {CREATE_IMPLICIT_ACCOUNT && ( - accountFound || !DISABLE_CREATE_ACCOUNT ? ( - + accountFound || + !DISABLE_CREATE_ACCOUNT ? ( + ) : ( ) } - // Logged in users always create a named account + // Logged in users always create a named account /> - } + )} - {CREATE_IMPLICIT_ACCOUNT && + {CREATE_IMPLICIT_ACCOUNT && ( - } - {CREATE_IMPLICIT_ACCOUNT && + )} + {CREATE_IMPLICIT_ACCOUNT && ( - } - {CREATE_IMPLICIT_ACCOUNT && + )} + {CREATE_IMPLICIT_ACCOUNT && ( - } - {CREATE_IMPLICIT_ACCOUNT && + )} + {CREATE_IMPLICIT_ACCOUNT && ( - } + )} { - const importString = decodeURIComponent(location.hash.substring(1)); - const hasAccountId = importString.includes('/'); - const seedPhrase = hasAccountId ? importString.split('/')[1] : importString; - const { secretKey } = parseSeedPhrase(seedPhrase); + const importString = decodeURIComponent( + location.hash.substring(1) + ); + const hasAccountId = + importString.includes('/'); + const seedPhrase = hasAccountId + ? importString.split('/')[1] + : importString; + const { secretKey } = + parseSeedPhrase(seedPhrase); return ( ); }} /> { - const importString = decodeURIComponent(location.hash.substring(1)); - const hasAccountId = importString.includes('/'); + const importString = decodeURIComponent( + location.hash.substring(1) + ); + const hasAccountId = + importString.includes('/'); return ( ); }} /> } + path="/authorized-apps" + render={() => ( + + )} /> } + path="/full-access-keys" + render={() => ( + + )} /> - {CREATE_USN_CONTRACT && + {CREATE_USN_CONTRACT && ( + + )} } + path="/swap" + component={SwapNear} + /> ( - +