);
}
-const Tab = styled.div`
- cursor: pointer;
- border-bottom: ${props => `2px solid ${props.theme.color}`};
- color: ${props => props.theme.color};
- h3 span {
- color: ${props => props.theme.textColor};
- }
-`;
-
TradingPositionSelector.defaultProps = {
value: TradingPosition.LONG,
- onChange: (value: TradingPosition) => {},
+ onChange: (_: TradingPosition) => {},
};
+
+interface TabProps {
+ text: React.ReactNode;
+ type: TradingPosition;
+ active: boolean;
+ onClick: (value: TradingPosition) => void;
+}
+
+function Tab(props: TabProps) {
+ const icon =
+ props.type === TradingPosition.LONG
+ ? faArrowAltCircleUp
+ : faArrowAltCircleDown;
+ const classes = props.active
+ ? props.type === TradingPosition.LONG
+ ? 'bg-long'
+ : 'bg-short'
+ : 'bg-muted';
+
+ return (
+
+ );
+}
diff --git a/src/app/components/TradingViewChart/index.tsx b/src/app/components/TradingViewChart/index.tsx
index 87afa19d9..e78ca4fc4 100644
--- a/src/app/components/TradingViewChart/index.tsx
+++ b/src/app/components/TradingViewChart/index.tsx
@@ -4,7 +4,6 @@
*
*/
import React, { useEffect, useState } from 'react';
-import { Asset } from 'types/asset';
import { Skeleton } from '../PageSkeleton';
enum Theme {
@@ -13,7 +12,7 @@ enum Theme {
}
export interface ChartContainerProps {
- asset: Asset;
+ symbol: string;
theme: Theme;
}
@@ -26,17 +25,18 @@ export function TradingViewChart(props: ChartContainerProps) {
const widget = new TradingView.widget({
width: 980,
height: 610,
- symbol: 'BITFINEX:BTCUSD' /* props.asset */,
+ symbol: props.symbol.toLowerCase(),
interval: '30' as any,
timezone: 'Etc/UTC',
- theme: props.theme,
+ // theme: 'Dark',
locale: 'en',
- toolbar_bg: '#f1f3f6',
+ toolbar_bg: '#171717',
enable_publishing: false,
allow_symbol_change: true,
container_id: 'trading-view-container',
autosize: true,
fullscreen: false,
+ studies_overrides: {},
disabled_features: [
'left_toolbar',
'header_compare',
@@ -49,15 +49,12 @@ export function TradingViewChart(props: ChartContainerProps) {
'go_to_date',
],
// enabled_features: ['study_templates'],
- loading_screen:
- props.theme === Theme.DARK
- ? { backgroundColor: 'rgb(0, 0, 0)' }
- : { backgroundColor: 'rgb(256, 256, 256)' },
+ loading_screen: { backgroundColor: '#171717' },
overrides: {
- 'paneProperties.background': '#131722',
+ 'paneProperties.background': '#171717',
'paneProperties.vertGridProperties.color': '#363c4e',
'paneProperties.horzGridProperties.color': '#363c4e',
- 'symbolWatermarkProperties.transparency': 90,
+ 'symbolWatermarkProperties.transparency': 200,
'scalesProperties.textColor': '#AAA',
'mainSeriesProperties.candleStyle.wickUpColor': '#336854',
'mainSeriesProperties.candleStyle.wickDownColor': '#7f323f',
@@ -70,12 +67,13 @@ export function TradingViewChart(props: ChartContainerProps) {
} catch (e) {
setHasCharts(false);
}
- }, [props.theme]);
+ }, [props.symbol]);
return (
{!hasCharts && (
<>
diff --git a/src/app/components/TutorialDialog/component/index.tsx b/src/app/components/TutorialDialog/component/index.tsx
new file mode 100644
index 000000000..081479b7c
--- /dev/null
+++ b/src/app/components/TutorialDialog/component/index.tsx
@@ -0,0 +1,112 @@
+import React, { useState } from 'react';
+import background from 'assets/images/tutorial/test.svg';
+import close from 'assets/images/tutorial/close.svg';
+import { Screen1 } from './screen1';
+import { Screen2 } from './screen2';
+import { Screen3 } from './screen3';
+import { Screen4 } from './screen4';
+import * as storage from 'utils/storage';
+
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+
+export function TutorialDialogComponent(props) {
+ const { t } = useTranslation();
+ const [mouseLeave, setMouseLeave] = useState(false);
+ const activeTutorial =
+ storage.local.getItem('tutorial_active') === 'true' &&
+ props.onNetwork === true
+ ? true
+ : false;
+ const [screen, setScreen] = useState(activeTutorial ? 2 : 1);
+
+ function changeScreen(num) {
+ setScreen(num);
+ }
+
+ function back() {
+ screen === 3 ? setScreen(4) : setScreen(1);
+ }
+
+ return (
+ <>
+
{
+ console.log('Mouse out');
+ if (screen === 2) {
+ setMouseLeave(true);
+ } else {
+ setMouseLeave(false);
+ }
+ }}
+ >
+
+
+
+
+
+
+
+ {screen !== 1 && (
+
back()}>
+
+
+ )}
+
+
+ {t(
+ translations.rskConnectTutorial.screens[screen.toString()]
+ .title,
+ )}
+
+
+
+ {/*
*/}
+ {screen === 1 && (
+
+ )}
+ {screen === 2 && (
+
+ )}
+ {screen === 3 &&
}
+ {screen === 4 &&
}
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/component/screen1/index.tsx b/src/app/components/TutorialDialog/component/screen1/index.tsx
new file mode 100644
index 000000000..2e7c3ff69
--- /dev/null
+++ b/src/app/components/TutorialDialog/component/screen1/index.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import rectangle from 'assets/images/tutorial/screen1_rectangle.svg';
+import browserIcon from 'assets/images/tutorial/brower_icon.svg';
+import mobileIcon from 'assets/images/tutorial/mobile_icon.svg';
+import hardwareIcon from 'assets/images/tutorial/hardware_icon.svg';
+import badger1 from 'assets/images/tutorial/badger_1.svg';
+import planet from 'assets/images/tutorial/planet.svg';
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+import * as storage from 'utils/storage';
+
+interface Props {
+ handleClick: (num: Number) => void;
+ onNetwork: boolean;
+ handleEngage: () => void;
+}
+
+export function Screen1(props: Props) {
+ const { t } = useTranslation();
+
+ function handleBrowserClick() {
+ storage.local.setItem('tutorial_active', 'true');
+ if (props.onNetwork === true) {
+ props.handleEngage();
+ } else {
+ props.handleClick(2);
+ }
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{t(translations.rskConnectTutorial.browser_wallet)}t
+
+
+
props.handleClick(4)}>
+
+
+
+
+
+
+
+
{t(translations.rskConnectTutorial.mobile_wallet)}
+
+
+
+
+
+
+
+
+
+
+
{t(translations.rskConnectTutorial.hardware_wallet)}
+
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/component/screen2/index.tsx b/src/app/components/TutorialDialog/component/screen2/index.tsx
new file mode 100644
index 000000000..460490434
--- /dev/null
+++ b/src/app/components/TutorialDialog/component/screen2/index.tsx
@@ -0,0 +1,226 @@
+import React, { useState, useEffect } from 'react';
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+import arm1 from 'assets/images/tutorial/arm_1.svg';
+import arm2 from 'assets/images/tutorial/arm_2.svg';
+import stepBox from 'assets/images/tutorial/step-box.svg';
+import leftArrow from 'assets/images/tutorial/left_arrow.svg';
+import rightArrow from 'assets/images/tutorial/right_arrow.svg';
+import engage from 'assets/images/tutorial/engage.svg';
+import speechBubble from 'assets/images/tutorial/speech_bubble.svg';
+import leftBox from 'assets/images/tutorial/left_box.svg';
+import rightBox from 'assets/images/tutorial/right_box.svg';
+import badgerBody from 'assets/images/tutorial/badger_body.svg';
+import crater from 'assets/images/tutorial/crater.svg';
+import { Icon } from '@blueprintjs/core';
+import { useContent } from '../../../../hooks/tutorial/useContent';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+
+interface Props {
+ onNetwork: boolean;
+ mouseLeave: boolean;
+ activeTutorial: boolean;
+ handleEngage: () => void;
+}
+
+export function Screen2(props: Props) {
+ const { t } = useTranslation();
+ const content = useContent();
+ const [cycle, setCycle] = useState(false);
+ const [step, setStep] = useState(props.activeTutorial === true ? 7 : 1);
+ const [speechText, setSpeechText] = useState(
+ props.activeTutorial ? content[7].speech : content[1].speech,
+ );
+
+ // const timer = ms => new Promise(res => setTimeout(res, ms));
+
+ // useEffect(() => {
+ // setTimeout(function () {
+ // setMoving(false);
+ // }, 1000);
+ // });
+
+ // async function printText(str) {
+ // await timer(500);
+ // setSpeechText('');
+ // for (let i = 0; i < str.length; i++) {
+ // setSpeechText(prevState => `${prevState}${str[i]}`);
+ // await timer(50);
+ // }
+ // }
+
+ function stepChange(num) {
+ setStep(num);
+ setCycle(false);
+ setSpeechText(content[num].speech);
+ // printText(content[num].speech);
+ }
+
+ useEffect(() => {
+ if (cycle) {
+ const interval = setInterval(() => {
+ setStep(prevState => (step < 7 ? prevState + 1 : 1));
+ setSpeechText(content[step === 7 ? 1 : step + 1].speech);
+ }, 3000);
+ return () => clearInterval(interval);
+ }
+ }, [step, cycle, content]);
+
+ useEffect(() => {
+ if (props.onNetwork) {
+ setCycle(false);
+ }
+ }, [props.onNetwork]);
+
+ useEffect(() => {
+ if (props.mouseLeave === true) {
+ setCycle(true);
+ }
+ }, [props.mouseLeave]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{speechText}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.title)}
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.network)}
+
+
RSK Mainnet
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.new_RPC)}
+
+
+ alert('Copied!')}
+ >
+
+ https://public-node.rsk.co
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.chain_Id)}
+
+
30
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.symbol)}
+
+
RBTC
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.explorer_url)}
+
+
+ alert('Copied!')}
+ >
+
+ https://explorer.rsk.co
+
+
+
+
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.step)}: 0{step}
+
+
+
+
+
(step > 1 ? stepChange(step - 1) : null)}
+ />
+
stepChange(1)}
+ >
+
stepChange(2)}
+ >
+
stepChange(3)}
+ >
+
stepChange(4)}
+ >
+
stepChange(5)}
+ >
+
stepChange(6)}
+ >
+
stepChange(7)}
+ >
+
(step < 7 ? stepChange(step + 1) : null)}
+ />
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/component/screen3/index.tsx b/src/app/components/TutorialDialog/component/screen3/index.tsx
new file mode 100644
index 000000000..06c12d042
--- /dev/null
+++ b/src/app/components/TutorialDialog/component/screen3/index.tsx
@@ -0,0 +1,38 @@
+import React, { useEffect } from 'react';
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+import badgerQr from 'assets/images/tutorial/badger_qr.svg';
+import { Sovryn } from 'utils/sovryn';
+
+export function Screen3() {
+ const { t } = useTranslation();
+ // const [wrongNetwork, setWrongNetwork] = useState(false);
+
+ function connect() {
+ Sovryn.connectTo('walletconnect');
+ }
+
+ useEffect(() => {
+ connect();
+ return function cleanup() {
+ var walletConnectModal = document.getElementById('walletconnect-wrapper');
+ walletConnectModal?.remove();
+ };
+ }, []);
+
+ // TODO: Handle wrong network error
+
+ return (
+ <>
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.speech_qr_code)}
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/component/screen4/index.tsx b/src/app/components/TutorialDialog/component/screen4/index.tsx
new file mode 100644
index 000000000..be9ca104c
--- /dev/null
+++ b/src/app/components/TutorialDialog/component/screen4/index.tsx
@@ -0,0 +1,176 @@
+import React, { useState } from 'react';
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+import arm1 from 'assets/images/tutorial/arm_1.svg';
+import arm2 from 'assets/images/tutorial/arm_2.svg';
+import stepBox from 'assets/images/tutorial/step-box.svg';
+import leftArrow from 'assets/images/tutorial/left_arrow.svg';
+import rightArrow from 'assets/images/tutorial/right_arrow.svg';
+import speechBubble from 'assets/images/tutorial/speech_bubble.svg';
+import leftBox from 'assets/images/tutorial/left_box.svg';
+import rightBox from 'assets/images/tutorial/right_box.svg';
+import badgerBody from 'assets/images/tutorial/badger_body.svg';
+import crater from 'assets/images/tutorial/crater.svg';
+import scanQR from 'assets/images/tutorial/scanQr.svg';
+import { Icon } from '@blueprintjs/core';
+import { useContent } from '../../../../hooks/tutorial/useContent';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+
+export function Screen4(props) {
+ const { t } = useTranslation();
+ const content = useContent();
+ const [step, setStep] = useState(1);
+ const [speechText, setSpeechText] = useState(content[1].mobileSpeech);
+
+ function stepChange(num) {
+ setStep(num);
+ setSpeechText(content[num].mobileSpeech);
+ // printText(content[num].speech);
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{speechText}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.title)}
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.network)}
+
+
RSK Mainnet
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.new_RPC)}
+
+
+ alert('Copied!')}
+ >
+
+ https://public-node.rsk.co
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.chain_Id)}
+
+
30
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.symbol)}
+
+
RBTC
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.explorer_url)}
+
+
+ alert('Copied!')}
+ >
+
+ https://explorer.rsk.co
+
+
+
+
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.step)}: 0{step}
+
+
+
+
+
(step > 1 ? stepChange(step - 1) : null)}
+ />
+
stepChange(1)}
+ >
+
stepChange(2)}
+ >
+
stepChange(3)}
+ >
+
stepChange(4)}
+ >
+
stepChange(5)}
+ >
+
stepChange(6)}
+ >
+
stepChange(7)}
+ >
+
(step < 7 ? stepChange(step + 1) : null)}
+ />
+
+
+
props.handleClick(3)}
+ >
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/componentMobile/index.tsx b/src/app/components/TutorialDialog/componentMobile/index.tsx
new file mode 100644
index 000000000..d734f0266
--- /dev/null
+++ b/src/app/components/TutorialDialog/componentMobile/index.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react';
+import { Screen1 } from './screen1';
+import { Screen2 } from './screen2';
+import { Screen3 } from './screen3';
+
+export function TutorialDialogMobileComponent(props) {
+ const [screen, setScreen] = useState(1);
+ const [wallet, setWallet] = useState('');
+
+ return (
+ <>
+
+ {screen === 1 && (
+
+ )}
+ {screen === 2 && }
+ {screen === 3 && (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/componentMobile/screen1/index.tsx b/src/app/components/TutorialDialog/componentMobile/screen1/index.tsx
new file mode 100644
index 000000000..f260ff748
--- /dev/null
+++ b/src/app/components/TutorialDialog/componentMobile/screen1/index.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import screen1Container from 'assets/images/tutorial/mobile_bg_1.svg';
+import close from 'assets/images/tutorial/close.svg';
+import metamaskLogo from 'assets/images/tutorial/metamask.svg';
+import rskLogo from 'assets/images/tutorial/rsk-wallet.svg';
+import trustLogo from 'assets/images/tutorial/trust-wallet.svg';
+
+export function Screen1(props) {
+ const wallets = [
+ {
+ name: 'rWallet',
+ logo: rskLogo,
+ click: () => {
+ alert('Deeplink');
+ }, //Deeplink
+ },
+ {
+ name: 'Metamask',
+ logo: metamaskLogo,
+ click: () => {
+ props.changeWallet('Metamask');
+ props.changeScreen(3);
+ }, //Go to tutorial
+ },
+ {
+ name: 'Trust',
+ logo: trustLogo,
+ click: () => {
+ props.changeWallet('Trust');
+ props.changeScreen(3);
+ }, //Go to tutorial
+ },
+ ];
+
+ const logos = wallets.map(item => (
+
+
+
{item.name}
+
+ ));
+
+ return (
+ <>
+
+
+
+
+
+
+
{logos}
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/componentMobile/screen2/index.tsx b/src/app/components/TutorialDialog/componentMobile/screen2/index.tsx
new file mode 100644
index 000000000..df9559c41
--- /dev/null
+++ b/src/app/components/TutorialDialog/componentMobile/screen2/index.tsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export function Screen2(props) {
+ return
Screen 2
;
+}
diff --git a/src/app/components/TutorialDialog/componentMobile/screen3/index.tsx b/src/app/components/TutorialDialog/componentMobile/screen3/index.tsx
new file mode 100644
index 000000000..a6d876d9b
--- /dev/null
+++ b/src/app/components/TutorialDialog/componentMobile/screen3/index.tsx
@@ -0,0 +1,162 @@
+import React, { useState } from 'react';
+import screen3Container from 'assets/images/tutorial/mobile_bg_3.svg';
+import close from 'assets/images/tutorial/close.svg';
+import leftArrow from 'assets/images/tutorial/mobile_left_arrow.svg';
+import rightArrow from 'assets/images/tutorial/mobile_right_arrow.svg';
+
+import { Icon } from '@blueprintjs/core';
+import { useContent } from '../../../../hooks/tutorial/useContent';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+
+export function Screen3(props) {
+ const { t } = useTranslation();
+ const content = useContent();
+ const [step, setStep] = useState(1);
+ const [speechText, setSpeechText] = useState(content[1].mobileSpeech);
+
+ function stepChange(num) {
+ setStep(num);
+ setSpeechText(content[num].mobileSpeech);
+ // printText(content[num].speech);
+ }
+
+ const deepLinks = {
+ Metamask: 'https://metamask.app.link/dapp/live.sovryn.app/',
+ Trust:
+ 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url=' +
+ 'live.sovryn.app',
+ };
+
+ return (
+ <>
+
+
+
+
Connecting to the RSK Network
+
+
props.handleClose}
+ >
+
+
+
+
+
+
+
+ Step: 0{step} - {speechText}
+
+
+
+
+
(step > 1 ? stepChange(step - 1) : null)}
+ />
+
stepChange(1)}
+ >
+
stepChange(2)}
+ >
+
stepChange(3)}
+ >
+
stepChange(4)}
+ >
+
stepChange(5)}
+ >
+
stepChange(6)}
+ >
+
(step < 6 ? stepChange(step + 1) : null)}
+ />
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.title)}
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.network)}:
+
+
RSK Mainnet
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.new_RPC)}:
+
+
+ alert('Copied!')}
+ >
+
+ https://public-node.rsk.co{' '}
+
+
+
+
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.chain_Id)}:
+
+
30
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.symbol)}:
+
+
RBTC
+
+
+
+ {t(translations.rskConnectTutorial.input_settings.explorer_url)}:
+
+
+ alert('Copied!')}
+ >
+
+ https://explorer.rsk.co{' '}
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/container/index.tsx b/src/app/components/TutorialDialog/container/index.tsx
new file mode 100644
index 000000000..c94ba6d52
--- /dev/null
+++ b/src/app/components/TutorialDialog/container/index.tsx
@@ -0,0 +1,89 @@
+// TUTORIALDIALOG CONTAINER
+// Logic for if dialog should render
+
+import React, { useState, useEffect, useCallback } from 'react';
+import { useIsConnected } from 'app/hooks/useAccount';
+import { Sovryn } from 'utils/sovryn';
+import { TutorialDialogComponent } from '../component';
+import { MobileNotReady } from '../mobileNotReady';
+import { currentChainId } from 'utils/classifiers';
+import { reactLocalStorage } from 'reactjs-localstorage';
+import * as storage from 'utils/storage';
+
+export function TutorialDialog() {
+ //Check if previously connected, currently connected to RSK, currently wallet is connected, closed before
+ const [show, setShow] = useState
(false);
+
+ const onNetwork =
+ window.ethereum && parseInt(window.ethereum.chainId) === currentChainId;
+
+ const checks = {
+ // previousUser:
+ // window.localStorage.getItem('connectedToRskBefore') === 'true',
+ connected: useIsConnected(),
+ closedBefore: storage.session.getItem('closedRskTutorial') === 'true',
+ };
+
+ const handleWalletConnection = useCallback(() => {
+ Sovryn.connect()
+ .then(() => {})
+ .catch(err => {
+ console.error(err);
+ });
+ }, []);
+
+ function handleEngage() {
+ handleWalletConnection();
+ storage.local.setItem('ongoing_tutorial', 'false');
+ storage.session.setItem('closedRskTutorial', 'true');
+ setShow(false);
+ }
+
+ useEffect(() => {
+ const shouldShow = Object.values(checks).every(check => check === false);
+ if (shouldShow) {
+ setShow(true);
+ } else {
+ setShow(false);
+ }
+ }, [checks]);
+
+ useEffect(() => {
+ const body = document.getElementsByTagName('body')[0];
+ if (show) {
+ body.classList.add('overflow-hidden');
+ } else {
+ body.classList.remove('overflow-hidden');
+ }
+ }, [show]);
+
+ function handleClose() {
+ const walletConectModal = document.getElementById('walletconnect-wrapper');
+ walletConectModal?.classList.remove('showWalletConnect');
+ walletConectModal?.classList.add('d-none');
+ reactLocalStorage.set('tutorial_active', 'false');
+ storage.session.setItem('closedRskTutorial', 'true');
+ setShow(false);
+ }
+
+ //On open, check session storage for preference
+
+ return (
+ <>
+ {show && (
+ <>
+
+
+
+
+
+
+ >
+ )}
+ >
+ );
+}
diff --git a/src/app/components/TutorialDialog/mobileNotReady/index.tsx b/src/app/components/TutorialDialog/mobileNotReady/index.tsx
new file mode 100644
index 000000000..4b8927933
--- /dev/null
+++ b/src/app/components/TutorialDialog/mobileNotReady/index.tsx
@@ -0,0 +1,100 @@
+import React, { useState, useEffect } from 'react';
+import MailchimpSubscribe from 'react-mailchimp-subscribe';
+import background from 'assets/images/tutorial/mobile-not-ready-bg.svg';
+import successBg from 'assets/images/tutorial/email_success_bg.svg';
+
+import { translations } from 'locales/i18n';
+import { useTranslation } from 'react-i18next';
+
+export function MobileNotReady() {
+ const { t } = useTranslation();
+ const s = translations.mobileNotReady;
+ const [status, setStatus] = useState('');
+ const [message, setMessage] = useState('');
+ const url = process.env.REACT_APP_MAILCHIMP;
+
+ const CustomForm = ({ status, message, onValidated }) => {
+ let email;
+ useEffect(() => setStatus(status), [status]);
+ useEffect(() => {
+ if (message) {
+ if (message.includes('already')) {
+ setStatus('success');
+ } else {
+ setMessage(message);
+ }
+ }
+ }, [message]);
+ const submit = () =>
+ email &&
+ email.value.indexOf('@') > -1 &&
+ onValidated({
+ EMAIL: email.value,
+ });
+
+ return (
+
+ (email = node)}
+ type="email"
+ placeholder="Your email"
+ />
+
+
+
+ );
+ };
+
+ return (
+ <>
+
+ {status !== 'success' && (
+ <>
+
+
+
+
{t(s.p1)}
+
+
{t(s.p2)}
+
+
+
(
+ subscribe(formData)}
+ />
+ )}
+ />
+
+ {status === 'sending' &&
Sending...
}
+ {status === 'error' && !message.includes('already') && (
+
{t(s.errorText)}
+ )}
+ {!status &&
{t(s.p3)}
}
+
+
+ >
+ )}
+
+ {status === 'success' && (
+ <>
+
+
+
+
{t(s.success)}
+
+
{t(s.successText)}
+
+
+ >
+ )}
+
+ >
+ );
+}
diff --git a/src/app/components/UnLendBalance/Loadable.tsx b/src/app/components/UnLendBalance/Loadable.tsx
deleted file mode 100644
index 8dd1af5a6..000000000
--- a/src/app/components/UnLendBalance/Loadable.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- *
- * Asynchronously loads the component for LendingPage
- *
- */
-
-import { lazyLoad } from 'utils/loadable';
-
-export const UnLendBalance = lazyLoad(
- () => import('./index'),
- module => module.UnLendBalance,
-);
diff --git a/src/app/components/UnLendBalance/index.tsx b/src/app/components/UnLendBalance/index.tsx
deleted file mode 100644
index 37d75be46..000000000
--- a/src/app/components/UnLendBalance/index.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- *
- * UnLendBalance
- *
- */
-import React, { useState } from 'react';
-import { Asset } from '../../../types/asset';
-import { WithdrawLentAmount } from '../../containers/WithdrawLentAmount/Loadable';
-
-interface Props {
- asset: Asset;
-}
-
-export function UnLendBalance(props: Props) {
- const [isOpen, setOpen] = useState(false);
- return (
-
-
- setOpen(false)}
- />
-
- );
-}
diff --git a/src/app/components/WhitelistedNotification/Loadable.tsx b/src/app/components/WhitelistedNotification/Loadable.tsx
new file mode 100644
index 000000000..52bab513b
--- /dev/null
+++ b/src/app/components/WhitelistedNotification/Loadable.tsx
@@ -0,0 +1,12 @@
+/**
+ *
+ * Asynchronously loads the component for WhitelistedNotification
+ *
+ */
+
+import { lazyLoad } from 'utils/loadable';
+
+export const WhitelistedNotification = lazyLoad(
+ () => import('./index'),
+ module => module.WhitelistedNotification,
+);
diff --git a/src/app/components/WhitelistedNotification/index.tsx b/src/app/components/WhitelistedNotification/index.tsx
new file mode 100644
index 000000000..6f89d1092
--- /dev/null
+++ b/src/app/components/WhitelistedNotification/index.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { Icon } from '@blueprintjs/core/lib/esm/components/icon/icon';
+import { useTranslation } from 'react-i18next';
+import { translations } from 'locales/i18n';
+import { useIsWhitelisted } from '../../hooks/whitelist/useIsWhitelisted';
+
+export function WhitelistedNotification() {
+ const isWhitelisted = useIsWhitelisted();
+ const { t } = useTranslation();
+
+ if (isWhitelisted) return <>>;
+
+ return (
+
+
+
+
+
+
{t(translations.whiteListedNotification.text)}
+
+
+ );
+}
diff --git a/src/app/components/WithdrawLentDialog/index.tsx b/src/app/components/WithdrawLentDialog/index.tsx
deleted file mode 100644
index 987747117..000000000
--- a/src/app/components/WithdrawLentDialog/index.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- *
- * WithdrawLentDialog
- *
- */
-
-import React, { useEffect, useState } from 'react';
-import { Dialog } from '@blueprintjs/core';
-import { SendTxResponseInterface } from 'app/hooks/useSendContractTx';
-import { SendTxProgress } from '../SendTxProgress';
-import { CloseModalButton } from '../CloseModalButton';
-import { AssetsDictionary } from '../../../utils/blockchain/assets-dictionary';
-import { Asset } from '../../../types/asset';
-import { bignumber } from 'mathjs';
-import { fromWei } from 'web3-utils';
-import { weiToBigInt } from '../../../utils/blockchain/math-helpers';
-
-interface Props {
- asset: Asset;
- isOpen: boolean;
- balance: string;
- amount: string;
- onChangeAmount: (value: string) => void;
- onConfirm: () => void;
- onClose: () => void;
- txState: Partial | null;
-}
-
-export function WithdrawLentDialog(props: Props) {
- const assetDetails = AssetsDictionary.get(props.asset);
- const fixedAmount = weiToBigInt(props.amount);
- const [isValid, setValid] = useState(false);
-
- useEffect(() => {
- if (fixedAmount) {
- setValid(
- bignumber(fixedAmount).greaterThan(0) &&
- bignumber(fixedAmount).lessThanOrEqualTo(props.balance),
- );
- }
- }, [props.balance, fixedAmount]);
-
- return (
-
- );
-}
diff --git a/src/app/containers/ActiveUserBorrows/index.tsx b/src/app/containers/ActiveUserBorrows/index.tsx
new file mode 100644
index 000000000..59a789d27
--- /dev/null
+++ b/src/app/containers/ActiveUserBorrows/index.tsx
@@ -0,0 +1,42 @@
+/**
+ *
+ * ActiveUserBorrows
+ *
+ */
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useAccount } from 'app/hooks/useAccount';
+import { useGetActiveLoans } from 'app/hooks/trading/useGetActiveLoans';
+import { SkeletonRow } from '../../components/Skeleton/SkeletonRow';
+import { ActiveBorrowTable } from '../../components/ActiveBorrowTable';
+import { translations } from 'locales/i18n';
+
+interface Props {}
+
+export function ActiveUserBorrows(props: Props) {
+ const { t } = useTranslation();
+ const account = useAccount();
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const { value, loading } = useGetActiveLoans(
+ account,
+ 0,
+ 1000,
+ 2,
+ false,
+ false,
+ );
+
+ if (loading && !value.length) {
+ return ;
+ }
+
+ if (!value.length && !loading) {
+ return (
+
+ {t(translations.activeUserBorrows.text)}
+
+ );
+ }
+
+ return <>{value.length && }>;
+}
diff --git a/src/app/containers/ActiveUserLoans/index.tsx b/src/app/containers/ActiveUserLoans/index.tsx
index 980494f4f..cb54a076c 100644
--- a/src/app/containers/ActiveUserLoans/index.tsx
+++ b/src/app/containers/ActiveUserLoans/index.tsx
@@ -6,10 +6,13 @@
import React from 'react';
import { useAccount } from 'app/hooks/useAccount';
import { useGetActiveLoans } from 'app/hooks/trading/useGetActiveLoans';
-import { ActiveLoanTable } from 'app/components/ActiveLoanTable';
+import { ActiveLoanTableContainer } from 'app/components/ActiveUserLoanContainer/components/ActiveLoanTableContainer';
import { SkeletonRow } from '../../components/Skeleton/SkeletonRow';
+import { InfoBox } from '../../components/InfoBox';
-interface Props {}
+interface Props {
+ loanType: number;
+}
export function ActiveUserLoans(props: Props) {
const account = useAccount();
@@ -18,7 +21,7 @@ export function ActiveUserLoans(props: Props) {
account,
0,
1000,
- 0,
+ props.loanType,
false,
false,
);
@@ -29,13 +32,40 @@ export function ActiveUserLoans(props: Props) {
if (!value.length && !loading) {
return (
-
- You do not have any active trades.
-
+ <>
+
+ You do not have any active trades.
+
+
+ Need help making a transaction? Read our guide on{' '}
+
+ how to trade and lend with Sovryn
+
+ .
+ >
+ }
+ localStorageRef="txHelpInfoBox"
+ />
+ >
);
}
return (
- <>{value.length && }>
+ <>
+ {value.length && (
+
+ )}
+ >
);
}
+
+ActiveUserLoans.defaultProps = {
+ loanType: 0,
+};
diff --git a/src/app/containers/AmountField/Loadable.tsx b/src/app/containers/AmountField/Loadable.tsx
new file mode 100644
index 000000000..4e10017b8
--- /dev/null
+++ b/src/app/containers/AmountField/Loadable.tsx
@@ -0,0 +1,12 @@
+/**
+ *
+ * Asynchronously loads the component for AmountField
+ *
+ */
+
+import { lazyLoad } from 'utils/loadable';
+
+export const AmountField = lazyLoad(
+ () => import('./index'),
+ module => module.AmountField,
+);
diff --git a/src/app/containers/AmountField/index.tsx b/src/app/containers/AmountField/index.tsx
new file mode 100644
index 000000000..cdab83981
--- /dev/null
+++ b/src/app/containers/AmountField/index.tsx
@@ -0,0 +1,56 @@
+/**
+ *
+ * AmountField
+ *
+ */
+
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { handleNumberInput } from 'utils/helpers';
+import { translations } from 'locales/i18n';
+import { InputField } from 'app/components/InputField';
+
+interface Props {
+ value: string;
+ onChange: (value: string) => void;
+ onMaxClicked: () => void;
+ allowNegative?: boolean;
+ hideMaxButton?: boolean;
+ rightElement?: React.ReactNode;
+}
+
+export function AmountField(props: Props) {
+ const { t } = useTranslation();
+ return (
+ <>
+
+ props.onChange(handleNumberInput(e, !props.allowNegative))
+ }
+ rightElement={
+ <>
+ {!props.hideMaxButton && !props.rightElement && (
+
+ )}
+ {props.rightElement && <>{props.rightElement}>}
+ >
+ }
+ />
+ >
+ );
+}
+
+AmountField.defaultProps = {
+ allowNegative: false,
+ hideMaxButton: false,
+ onMaxClicked: () => {},
+};
diff --git a/src/app/containers/TradePage/Loadable.tsx b/src/app/containers/BorrowHistory/Loadable.tsx
similarity index 61%
rename from src/app/containers/TradePage/Loadable.tsx
rename to src/app/containers/BorrowHistory/Loadable.tsx
index 55c8733b7..d149431a4 100644
--- a/src/app/containers/TradePage/Loadable.tsx
+++ b/src/app/containers/BorrowHistory/Loadable.tsx
@@ -1,6 +1,6 @@
/**
*
- * Asynchronously loads the component for TradePage
+ * Asynchronously loads the component for BorrowHistory
*
*/
@@ -8,8 +8,8 @@ import React from 'react';
import { lazyLoad } from 'utils/loadable';
import { PageSkeleton } from 'app/components/PageSkeleton';
-export const TradePage = lazyLoad(
+export const BorrowHistory = lazyLoad(
() => import('./index'),
- module => module.TradePage,
+ module => module.BorrowHistory,
{ fallback: },
);
diff --git a/src/app/containers/BorrowHistory/index.tsx b/src/app/containers/BorrowHistory/index.tsx
new file mode 100644
index 000000000..1e3171ab8
--- /dev/null
+++ b/src/app/containers/BorrowHistory/index.tsx
@@ -0,0 +1,169 @@
+/**
+ *
+ * BorrowHistory
+ *
+ */
+
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useTable, useSortBy } from 'react-table';
+import { Icon, Text } from '@blueprintjs/core';
+import { translations } from 'locales/i18n';
+
+import { BorrowAmount } from '../../components/ActiveBorrowTable/BorrowAmount';
+import { AssetsDictionary } from '../../../utils/dictionaries/assets-dictionary';
+import { CollateralAmount } from '../../components/ActiveBorrowTable/CollateralAmount';
+import { DisplayDate } from '../../components/ActiveUserLoanContainer/components/DisplayDate';
+import { useGetContractPastEvents } from '../../hooks/useGetContractPastEvents';
+import { weiToFixed } from '../../../utils/blockchain/math-helpers';
+import { SkeletonRow } from '../../components/Skeleton/SkeletonRow';
+
+interface Props {}
+
+export function BorrowHistory(props: Props) {
+ const { t } = useTranslation();
+
+ const { events, loading } = useGetContractPastEvents(
+ 'sovrynProtocol',
+ 'Borrow',
+ );
+
+ const columns = React.useMemo(
+ () => [
+ {
+ Header: 'Borrowed',
+ accessor: 'borrowAmount',
+ sortType: 'alphanumeric',
+ sortable: true,
+ },
+ {
+ Header: 'Collateral',
+ accessor: 'collateralAmount',
+ sortType: 'alphanumeric',
+ sortable: true,
+ },
+ {
+ Header: 'Interest APR',
+ accessor: 'interestAPR',
+ sortable: true,
+ },
+ {
+ Header: 'Date',
+ accessor: 'timestamp',
+ sortable: true,
+ },
+ {
+ Header: '',
+ accessor: 'actions',
+ },
+ ],
+ [],
+ );
+ const data = React.useMemo(() => {
+ return events.map(item => {
+ const timestamp = String(
+ new Date((item as any).eventDate).getTime() / 1e3,
+ );
+ return {
+ id: item.returnValues.loanId,
+ borrowAmount: (
+
+ ),
+ collateralAmount: (
+
+ ),
+ interestAPR: <>{weiToFixed(item.returnValues.interestRate, 2)} %>,
+ timestamp: ,
+ };
+ });
+ }, [events]);
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ rows,
+ prepareRow,
+ } = useTable({ columns, data }, useSortBy);
+ return (
+
+
+
+ {headerGroups.map(headerGroup => (
+
+ {headerGroup.headers.map(column => (
+
+
+ {column.render('Header')}
+ {column.sortable && (
+
+ {column.isSorted ? (
+ column.isSortedDesc ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
+
+ )}
+
+ |
+ ))}
+
+ ))}
+
+
+ {rows.map(row => {
+ prepareRow(row);
+ return (
+
+ {row.cells.map(cell => {
+ return (
+
+ {cell.render('Cell')}
+ |
+ );
+ })}
+
+ );
+ })}
+ {rows.length === 0 && !loading && (
+
+ {t(translations.borrowHistory.no_items)} |
+
+ )}
+ {rows.length === 0 && loading && (
+
+
+
+ |
+
+ )}
+
+
+
+ );
+}
diff --git a/src/app/containers/CloseTradingPositionHandler/index.tsx b/src/app/containers/CloseTradingPositionHandler/index.tsx
index e90e53782..ea45200ca 100644
--- a/src/app/containers/CloseTradingPositionHandler/index.tsx
+++ b/src/app/containers/CloseTradingPositionHandler/index.tsx
@@ -4,8 +4,7 @@
*
*/
-import React, { useState, useEffect } from 'react';
-import { Dialog } from '@blueprintjs/core';
+import React, { useEffect, useState } from 'react';
import { ActiveLoan } from '../../hooks/trading/useGetActiveLoans';
import { FormSelect } from '../../components/FormSelect';
import { SendTxProgress } from '../../components/SendTxProgress';
@@ -15,7 +14,12 @@ import { useAccount } from '../../hooks/useAccount';
import { weiTo18 } from '../../../utils/blockchain/math-helpers';
import { symbolByTokenAddress } from '../../../utils/blockchain/contract-helpers';
import { useIsAmountWithinLimits } from '../../hooks/useIsAmountWithinLimits';
-import { handleNumberInput } from '../../../utils/helpers';
+import { Dialog } from '../Dialog/Loadable';
+import { DummyField } from '../../components/DummyField';
+import { AmountField } from '../AmountField';
+import { DialogButton } from '../../components/DialogButton';
+import { AssetWalletBalance } from '../../components/AssetWalletBalance';
+import { Asset } from '../../../types/asset';
interface Props {
item: ActiveLoan;
@@ -38,12 +42,16 @@ export function CloseTradingPositionHandler(props: Props) {
const [amount, setAmount] = useState();
const [isCollateral, setIsCollateral] = useState(false);
- const [options] = useState(getOptions(props.item));
+ const [options, setOptions] = useState(getOptions(props.item));
useEffect(() => {
setAmount(weiTo18(props.item.collateral));
}, [props.item.collateral]);
+ useEffect(() => {
+ setOptions(getOptions(props.item));
+ }, [props.item]);
+
const weiAmount = useWeiAmount(amount);
const { send, ...rest } = useCloseWithSwap(
@@ -63,44 +71,33 @@ export function CloseTradingPositionHandler(props: Props) {
const withdrawAll = amount === weiTo18(props.item.collateral);
return (
-