From 13806c11df2e078cb788f19759c11046e0f750b3 Mon Sep 17 00:00:00 2001 From: JenkJS Date: Mon, 23 Jan 2023 12:28:21 +0300 Subject: [PATCH 01/12] feat: add_liquid not empty pool --- .idea/workspace.xml | 26 ++- src/app/app.tsx | 10 +- .../components/PoolList/PoolCard/PoolCard.tsx | 6 +- .../containers/AddLiquidity/AddLiquidity.tsx | 209 +++++++++++++----- .../Pools/containers/AddLiquidity/index.scss | 120 ++++------ .../containers/CreatePool/CreatePool.tsx | 72 +++--- .../Pools/containers/CreatePool/index.scss | 88 ++++---- .../Pools/containers/PoolList/index.scss | 8 +- .../Pools/containers/TradePool/TradePool.tsx | 164 +++++++------- .../Pools/containers/TradePool/index.scss | 13 ++ src/app/core/appUtils.ts | 17 +- src/app/shared/components/AssetsContainer.tsx | 23 ++ src/app/shared/components/AssetsSection.tsx | 27 +++ src/app/shared/components/Container.tsx | 25 +++ src/app/shared/components/Input.tsx | 16 +- src/app/shared/components/Section.tsx | 9 +- src/app/shared/components/Window.tsx | 1 + src/app/shared/components/index.ts | 3 + src/app/shared/constants/common.tsx | 10 + src/app/shared/hooks/useInput.tsx | 7 +- src/app/shared/icons/icon-swap.svg | 21 ++ src/app/shared/icons/index.ts | 2 + 22 files changed, 565 insertions(+), 312 deletions(-) create mode 100644 src/app/shared/components/AssetsContainer.tsx create mode 100644 src/app/shared/components/AssetsSection.tsx create mode 100644 src/app/shared/components/Container.tsx create mode 100644 src/app/shared/icons/icon-swap.svg diff --git a/.idea/workspace.xml b/.idea/workspace.xml index a2beada..681d006 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,15 +5,27 @@ + + + + + + + - - - - + + + + + + + + + diff --git a/src/app/app.tsx b/src/app/app.tsx index 585e935..d6555cd 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -14,7 +14,7 @@ import { ToastContainer } from "react-toastify"; import { Scrollbars } from "react-custom-scrollbars"; import "./styles"; -import {PoolsContainer, CreatePool, AddLiquidity, TradePool, WithdrawPool} from "@app/containers"; +import {PoolsContainer, CreatePool, AddLiquidity, TradePool} from "@app/containers"; import { ROUTES } from "@app/shared/constants"; const trackStyle = css` z-index: 999; @@ -39,10 +39,10 @@ const routes = [ path: ROUTES.POOLS.TRADE_POOL, element: , }, - { - path: ROUTES.POOLS.WITHDRAW_POOL, - element: , - }, + // { + // path: ROUTES.POOLS.WITHDRAW_POOL, + // element: , + // }, ]; const App = () => { diff --git a/src/app/containers/Pools/components/PoolList/PoolCard/PoolCard.tsx b/src/app/containers/Pools/components/PoolList/PoolCard/PoolCard.tsx index 599db1b..86bb5d2 100644 --- a/src/app/containers/Pools/components/PoolList/PoolCard/PoolCard.tsx +++ b/src/app/containers/Pools/components/PoolList/PoolCard/PoolCard.tsx @@ -6,7 +6,7 @@ import {ROUTES_PATH} from "@app/shared/constants"; import {useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; import * as mainActions from "@app/containers/Pools/store/actions"; -import {Button} from "@app/shared/components"; +import { Button, Section } from "@app/shared/components"; import {styled} from "@linaria/react"; import {toast} from "react-toastify"; @@ -43,7 +43,7 @@ export const PoolCard = ({data, assets}:PoolCardType) => { return (
-
+
{nameToken1} / {nameToken2}
fee: {getPoolKind(data.kind)}
@@ -63,7 +63,7 @@ export const PoolCard = ({data, assets}:PoolCardType) => { {data.ctl ? : null} -
+
); }; diff --git a/src/app/containers/Pools/containers/AddLiquidity/AddLiquidity.tsx b/src/app/containers/Pools/containers/AddLiquidity/AddLiquidity.tsx index be9cd89..6d2a0a6 100644 --- a/src/app/containers/Pools/containers/AddLiquidity/AddLiquidity.tsx +++ b/src/app/containers/Pools/containers/AddLiquidity/AddLiquidity.tsx @@ -1,8 +1,17 @@ import React, { useEffect, useMemo, useState } from "react"; import "./index.scss"; -import { IAddLiquidity } from "@core/types"; -import { Button, Input, Title } from "@app/shared/components"; -import {fromGroths, setDataRequest, toGroths} from "@core/appUtils"; +import { IAddLiquidity, IPoolCard, IPredict } from "@core/types"; +import { + AssetsContainer, + AssetsSection, + Button, + Container, + Input, + Section, + Title, + Window +} from "@app/shared/components"; +import { emptyPredict, fromGroths, numFormatter, setDataRequest, toGroths } from "@core/appUtils"; import { useDispatch, useSelector } from "react-redux"; import * as mainActions from "@app/containers/Pools/store/actions"; import { toast } from "react-toastify"; @@ -10,86 +19,178 @@ import { selectCurrentPool, selectPredirect, } from "@app/containers/Pools/store/selectors"; import {useInput} from "@app/shared/hooks"; +import { titleSections } from "@app/shared/constants"; +import { IconSwap } from "@app/shared/icons"; export const AddLiquidity = () => { const data = useSelector(selectCurrentPool()); const predictData = useSelector(selectPredirect()); + const [currentToken, setCurrentToken] = useState(data.aid1); const amountInput_aid1 = useInput(0) const amountInput_aid2 = useInput(0) const [requestData, setRequestData] = useState(null); + const [isSwap, setIsSwap] = useState(false); + const [isEmpty, setIsEmpty] = useState(true) const dispatch = useDispatch(); + + useEffect(()=>{ + setIsEmpty(onCheckEmptyPool(data)) + },[]) + + const getValueInput_1 = () => { + if(isEmpty){ + return toGroths(Number(amountInput_aid1.value)) + } + return currentToken === data.aid2 ? '0' : toGroths(Number(amountInput_aid1.value)) + } + const getValueInput_2 = () => { + if(isEmpty){ + return toGroths(Number(amountInput_aid2.value)) + } + return currentToken === data.aid1 ? '0' : toGroths(Number(amountInput_aid2.value)) + } + + + useEffect(()=>{ + setCurrentToken(!isSwap?data.aid1 : data.aid2) + },[isSwap]) useEffect(() => { setRequestData({ aid1: data.aid1, aid2: data.aid2, kind: data.kind, - val1: toGroths(Number(amountInput_aid1.value)), - val2: toGroths(Number(amountInput_aid2.value)), + val1: getValueInput_1(), + val2: getValueInput_2(), }); - }, [amountInput_aid2.value, amountInput_aid2.value]); + }, [amountInput_aid1.value, amountInput_aid2.value, isSwap]); + console.log(requestData, isSwap) + + + useEffect(()=>{ + if(predictData){ + if(currentToken === data.aid1){ + !emptyPredict(predictData, amountInput_aid1.value) ? amountInput_aid2.onPredict(fromGroths(predictData.tok2)) : amountInput_aid2.onPredict(0); + } else { + !emptyPredict(predictData, amountInput_aid2.value) ? amountInput_aid1.onPredict(fromGroths(predictData.tok1)) : amountInput_aid1.onPredict(0); + } + } + },[isSwap, amountInput_aid1.value, amountInput_aid2.value]) + + const handleChange = () => { + setIsSwap(!isSwap) + } useMemo(() => { if (amountInput_aid1.value !== 0 && amountInput_aid1.value !== 0) { dispatch(mainActions.onAddLiquidity.request(requestData)); } }, [requestData]); + const onAddLiquidity = (data: IAddLiquidity): void => { dispatch(mainActions.onAddLiquidity.request(setDataRequest(data))); }; - + function onCheckEmptyPool(pool:IPoolCard){ + return !(pool.tok1 || pool.tok2 !== 0) + } + const onPredictValue = (value, swap:boolean, predict: IPredict) => { + if(!predict || value.value==0){ + return 0 + } + if(swap){ + return fromGroths(predict.tok1) + } + return fromGroths(predict.tok2) + } + const currentInput = isSwap ? amountInput_aid2 : amountInput_aid1 + const calculated = onPredictValue(currentInput, isSwap, predictData) return ( -
+ Add Liquidity -
- Select Pair -
-
- amountInput_aid1.onChange(e)} - /> -
- {data.metadata1.N} -
-
-
- amountInput_aid2.onChange(e)} + + {isEmpty ?( + {/*Select Pair*/} +
+ + amountInput_aid1.onChange(e)} + /> +
+ {data.metadata1.N} +
+
+
+
+ + amountInput_aid2.onChange(e)} + /> +
+ {data.metadata2.N} +
+
+
+
): ( + +
+ + currentInput.onChange(e)} + /> +

{isSwap ? data.metadata2.N : data.metadata1.N}

+
+
+ +
+ + +

{isSwap ? data.metadata1.N : data.metadata2.N}

+
- /> -
- {data.metadata2.N} +
+
+ )} +
+
+
{data.metadata1.N}:
+
+ {!predictData || currentInput.value == 0 ? 0 : fromGroths(predictData.tok1)}
-
-
-
{data.metadata1.N}:
-
- {predictData ? fromGroths(predictData.tok1) : "0"} -
-
-
-
{data.metadata2.N}:
-
- {predictData ? fromGroths(predictData.tok2) : "0"} +
+
{data.metadata2.N}:
+
+ {!predictData || currentInput.value == 0 ? 0 : fromGroths(predictData.tok2)} +
-
-
-
Ctl:
-
- {predictData ? predictData.ctl : "0"} +
+
Ctl:
+
+ {!predictData || currentInput.value == 0 ? 0 : fromGroths(predictData.ctl)} +
-
- -
- -
-
-
+ + + + ); }; diff --git a/src/app/containers/Pools/containers/AddLiquidity/index.scss b/src/app/containers/Pools/containers/AddLiquidity/index.scss index c0a0209..21d05f1 100644 --- a/src/app/containers/Pools/containers/AddLiquidity/index.scss +++ b/src/app/containers/Pools/containers/AddLiquidity/index.scss @@ -1,74 +1,46 @@ -.create-pool-wrapper{ - width: 600px; - margin: 0 auto; - h2{ - text-align: center; - } - - .create-pool-assets-container{ - - .assets-selector-wrapper{ - margin: 32px 0; - .asset-selector{ - display: flex; - align-items: center; - margin-bottom: 24px; - justify-content: space-between; - .select-wrapper{ - width: 130px; - height: 41px; - border: 1px solid; - } - input{ - width: 450px; - } - } - } - - .custom-select { - &__control{ - background-color: var(--color-darkest-blue) !important; - } - &__menu{ - background-color: var(--color-darkest-blue) !important; - } - &__single-value{ - color: white; - } - } - - .price-wrapper{ - display: flex; - font-size: 18px; - .price-title{ - margin-right: 24px; - } - .price-value{ - color:var( --color-green) - } - } - - - .fees-container{ - margin: 32px 0; - .fees-wrapper{ - border: 2px solid var( --color-green); - padding: 24px; - .information{ - margin-bottom: 24px; - } - } - - } - } - - .select-wrapper{ - .select-content{ - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - } - } -} + // + //.custom-select { + // &__control{ + // background-color: var(--color-darkest-blue) !important; + // } + // &__menu{ + // background-color: var(--color-darkest-blue) !important; + // } + // &__single-value{ + // color: white; + // } + //} + // + //.price-wrapper{ + // display: flex; + // font-size: 18px; + // .price-title{ + // margin-right: 24px; + // } + // .price-value{ + // color:var( --color-green) + // } + //} + // + // + //.fees-container{ + // margin: 32px 0; + // .fees-wrapper{ + // border: 2px solid var( --color-green); + // padding: 24px; + // .information{ + // margin-bottom: 24px; + // } + // } + // + //} + // + //.select-wrapper{ + // .select-content{ + // width: 100%; + // height: 100%; + // display: flex; + // align-items: center; + // justify-content: center; + // } + //} diff --git a/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx b/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx index 0ba7dba..11b873d 100644 --- a/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx +++ b/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx @@ -1,13 +1,26 @@ import React, {useCallback, useEffect, useState} from "react"; import "./index.scss"; -import {Button, Title} from "@app/shared/components"; +import { AssetsContainer, Button, Section, Title, Window, Container } from "@app/shared/components"; import {useDispatch, useSelector} from "react-redux"; import {selectAssetsList, selectPoolsList, selectTxStatus} from "@app/containers/Pools/store/selectors"; import Select from 'react-select' import {IAsset, ICreatePool, TxStatus} from "@core/types"; -import {kindSelect, ROUTES_PATH} from "@app/shared/constants"; +import { kindSelect, ROUTES_PATH, titleSections } from "@app/shared/constants"; import {useNavigate} from "react-router-dom"; import * as mainActions from "@app/containers/Pools/store/actions"; +import { styled } from "@linaria/react"; + +// const Container = styled.div` +// display: flex; +// flex-direction: column; +// align-items: center; +// margin: 50px 0; +// width: 100%; +// min-height: 600px; +// height: 100%; +// justify-content: space-between; +// `; + export const CreatePool = () => { const assetsList = useSelector(selectAssetsList()); @@ -101,38 +114,25 @@ export const CreatePool = () => { } return ( -
+ Create Pool - -
- Select Pair -
-
- {/**/} -
- */} -
- + +
+ amountInput.onChange(e)} + /> +

{isSwap ? data.metadata2.N : data.metadata1.N}

+ +
+ +
+ amountInput.onChange(e)} + disabled + pallete='purple' + variant='amount' + style={{cursor: 'default', color: '--var(color-purple)', opacity: 1}} + value={ emptyPredict(predictData, amountInput.value) ? '0' : fromGroths(predictData.pay) } /> -
-
diff --git a/src/app/containers/Pools/containers/CreatePool/index.scss b/src/app/containers/Pools/containers/CreatePool/index.scss index 9df1d5d..1b07b60 100644 --- a/src/app/containers/Pools/containers/CreatePool/index.scss +++ b/src/app/containers/Pools/containers/CreatePool/index.scss @@ -1,19 +1,19 @@ .custom-select { - &_container{ - max-width: 412px; - width: 100%; - } &__control{ background-color: rgba(255, 255, 255, 0.05) !important; border: none !important; border-radius: 10px; + max-width: 412px !important; + width: 100% !important; + min-width: 300px !important; &--menu-is-open{ box-shadow:( 0 0 0 1px ) var(--color-green) !important; } } &__menu{ - background-color: rgba(255, 255, 255, 0.05) !important; + background-color: rgba(0, 0, 0, 0.9) !important; + box-shadow:( 0 0 0 1px ) var(--color-green) !important; &-list::-webkit-scrollbar { width: 4px; height: 0px; @@ -54,6 +54,73 @@ color: white !important; } } + .custom-filter{ + &__control{ + background-color: rgba(255, 255, 255, 0.05) !important; + border: none !important; + border-radius: 10px; + max-width: 400px !important; + width: 100% !important; + min-width: 300px !important; + &--menu-is-open, &--is-focused{ + box-shadow:( 0 0 0 1px ) var(--color-green) !important; + } + } + &__indicator-separator{ + display: none; + } + &__multi-value{ + background-color: rgba(255, 255, 255, 0.05) !important; + } + &__value-container{ + flex-wrap: nowrap !important; + } + &__multi-value__label{ + color: white !important; + } + &__menu{ + background-color: rgba(0, 0, 0, 0.9) !important; + box-shadow:( 0 0 0 1px ) var(--color-green) !important; + &-list::-webkit-scrollbar { + width: 4px !important; + height: 0px !important; ; + } + &-list::-webkit-scrollbar-track { + background-color: rgba(255, 255, 255, 0.2) !important; + border-radius: 3px !important; + } + &-list::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.2) !important; + border-radius: 3px !important; + } + &-list::-webkit-scrollbar-thumb:hover { + background: var(--color-green) !important; + border-radius: 3px !important; + } + } + &__option{ + &--is-focused, &--is-selected{ + background-color: var(--color-green) !important; + color: black !important; + } + } + &__single-value{ + font-family: 'Proxima Nova' !important; + font-style: normal !important; + font-weight: 400 !important; + font-size: 14px !important; + line-height: 14px !important; + color: white !important; + } + &__placeholder{ + font-family: 'Proxima Nova' !important; + font-style: normal !important; + font-weight: 400 !important; + font-size: 14px !important; + line-height: 14px !important; + color: white !important; + } + } .price-wrapper{ display: flex; diff --git a/src/app/containers/Pools/containers/PoolList/PoolsList.tsx b/src/app/containers/Pools/containers/PoolList/PoolsList.tsx index 83fe975..f349540 100644 --- a/src/app/containers/Pools/containers/PoolList/PoolsList.tsx +++ b/src/app/containers/Pools/containers/PoolList/PoolsList.tsx @@ -1,22 +1,61 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import "./index.scss"; -import { Input, Button, Title } from "@app/shared/components"; +import { Button, Title, Container, ReactSelect, Window } from "@app/shared/components"; import { PoolCard } from "@app/containers/Pools/components/PoolList"; import { useNavigate } from "react-router-dom"; import { ROUTES_PATH, SORT } from "@app/shared/constants"; import { useDispatch, useSelector } from "react-redux"; import { - selectAssetsList, - selectPoolsList, + selectAssetsList, selectOptions, + selectPoolsList } from "@app/containers/Pools/store/selectors"; import { setFilter } from "@app/containers/Pools/store/actions"; import * as mainActions from "@app/containers/Pools/store/actions"; +import { getFilterPools } from "@core/appUtils"; +import { styled } from "@linaria/react"; +const Sort = styled.div` + max-width: 450px; + width: 100%; + display: flex; + justify-content: space-evenly; +` +const HeaderContainer = styled.div` +display: flex; +width: 100%; +justify-content: space-between; +` + +const PoolList = styled.div` +{ + margin: 16px 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + //grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: repeat(5, 1fr); + grid-column-gap: 10px; + grid-row-gap: 10px; + min-width: 954px; + width: 100%; +}` + +const CreateButton = styled.div` +position: absolute; + top:0; + right: 0; +` + +const HeaderWrapperSort = styled.div` + display: flex; + justify-content: space-between; +` export const PoolsList = () => { const navigate = useNavigate(); - - const poolsList = useSelector(selectPoolsList()); + const data = useSelector(selectPoolsList()); const assetsList = useSelector(selectAssetsList()); + const options = useSelector(selectOptions()); + const [poolsList, setPoolList] = useState(data); + const [filtered, setFiltered] = useState([]); const dispatch = useDispatch(); @@ -29,24 +68,45 @@ export const PoolsList = () => { dispatch(mainActions.loadAppParams.request(null)) }; + const onFilter = (value) => { + setFiltered(value) + } + useMemo(()=>{ + setPoolList(getFilterPools(filtered,data)) + },[data, filtered]) + + console.log({ poolsList }) + + // useEffect(()=>{ + // if(filtered.length === 0){ + // // setPoolList( data.filter(el, idx=>{ + // // el.aid1 || el.aid2 === filtered[idx] + // // })) + // setPoolList(data) + // } else { + // } + // },[filtered, poolsList]) + return ( -
- Pools -
-
- - {SORT.map((el) => - )} -
- -
- -
- {poolsList.map((item, idx) => ( + + + + + + + {SORT.map((el) => + )} + + + + + + {poolsList.length > 0 ? poolsList.map((item, idx) => ( - ))} -
-
+ )) :
Empty
} + + + ); }; diff --git a/src/app/containers/Pools/containers/PoolList/index.scss b/src/app/containers/Pools/containers/PoolList/index.scss index b786e2c..5edcd5a 100644 --- a/src/app/containers/Pools/containers/PoolList/index.scss +++ b/src/app/containers/Pools/containers/PoolList/index.scss @@ -1,4 +1,5 @@ .pool-list-container-wrapper { + margin: 0 3px; h3 { display: flex; justify-content: center; @@ -11,7 +12,7 @@ .poop-list-sort-wrapper { display: flex; - max-width: 500px; + max-width: 800px; width: 100%; justify-content: space-between; } @@ -29,7 +30,8 @@ .pool-list-wrapper { margin: 16px 0; display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + //grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-template-columns: 1fr 1fr 1fr; grid-template-rows: repeat(5, 1fr); grid-column-gap: 10px; grid-row-gap: 10px; diff --git a/src/app/containers/Pools/containers/TradePool/TradePool.tsx b/src/app/containers/Pools/containers/TradePool/TradePool.tsx index 302c6aa..7198dc0 100644 --- a/src/app/containers/Pools/containers/TradePool/TradePool.tsx +++ b/src/app/containers/Pools/containers/TradePool/TradePool.tsx @@ -1,7 +1,17 @@ import React, { useEffect, useMemo, useState } from "react"; import { ITrade } from "@core/types"; import { emptyPredict, fromGroths, setDataRequest, toGroths } from "@core/appUtils"; -import { AssetsContainer, AssetsSection, Button, Input, Section, Title, Window, Container } from "@app/shared/components"; +import { + AssetsContainer, + AssetsSection, + Button, + Input, + Section, + Title, + Window, + Container, + BackButton +} from "@app/shared/components"; import { useInput } from "@app/shared/hooks"; import * as mainActions from "@app/containers/Pools/store/actions"; import { useDispatch, useSelector } from "react-redux"; @@ -11,7 +21,8 @@ import { selectPredirect, } from "@app/containers/Pools/store/selectors"; import { IconSwap } from "@app/shared/icons"; -import { titleSections } from "@app/shared/constants"; +import { ROUTES, titleSections } from "@app/shared/constants"; +import { useNavigate } from "react-router-dom"; export const TradePool = () => { @@ -24,6 +35,10 @@ export const TradePool = () => { const amountInput = useInput(0); const [requestData, setRequestData] = useState(null); const dispatch = useDispatch(); + const navigate = useNavigate() + + const tokenName_1 = `${data.aid1} ${data.metadata1.N}`; + const tokenName_2 = `${data.aid2} ${data.metadata2.N}`; console.log({ isSwap, currentToken }) @@ -53,11 +68,13 @@ export const TradePool = () => { const onTrade = (data: ITrade) => { dispatch(mainActions.onTradePool.request(setDataRequest(data))); }; + const onPreviousClick = () => { + navigate(ROUTES.POOLS.BASE); + }; return ( - - Trade + - +
{ pallete='blue' onChange={(e) => amountInput.onChange(e)} /> -

{isSwap ? data.metadata2.N : data.metadata1.N}

+

{isSwap ? tokenName_2 :tokenName_1}

@@ -81,7 +98,7 @@ export const TradePool = () => { style={{cursor: 'default', color: '--var(color-purple)', opacity: 1}} value={ emptyPredict(predictData, amountInput.value) ? '0' : fromGroths(predictData.pay) } /> -

{isSwap ? data.metadata1.N : data.metadata2.N}

+

{isSwap ? tokenName_1 :tokenName_2}

diff --git a/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx b/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx index 6897af0..5b5577c 100644 --- a/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx +++ b/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx @@ -1,68 +1,90 @@ -// import React, { useMemo, useState} from 'react'; -// import {useDispatch, useSelector} from "react-redux"; -// import {selectCurrentPool, selectPredirect} from "@app/containers/Pools/store/selectors"; -// import * as mainActions from "@app/containers/Pools/store/actions"; -// import {Button, Input, Title} from "@app/shared/components"; -// import {onWithdraw} from "@app/containers/Pools/store/actions"; -// import {fromGroths} from "@core/appUtils"; -// -// export const WithdrawPool = () => { -// const data = useSelector(selectCurrentPool()); -// const predictData = useSelector(selectPredirect()); -// const [requestData, setRequestData] = useState(null) -// -// const dispatch = useDispatch() -// -// useMemo(() => { -// dispatch(mainActions.onWithdraw.request(data)); -// }, [requestData]); -// return ( -//
-// Withdraw -//
-// Select token -//
-//
-//
{data.metadata1.N}:
-//
{fromGroths(Number(data.tok1))}
-//
-//
-//
{data.metadata2.N}:
-//
{fromGroths(Number(data.tok2))}
-//
-//
-//
Buy:
-//
-// {predictData ? predictData.buy : "0"} -//
-//
-//
-//
Fee-dao:
-//
-// {predictData ? predictData.fee_dao : "0"} -//
-//
-//
-//
Fee-pool:
-//
-// {predictData ? predictData.fee_pool : "0"} -//
-//
-//
-//
Pay:
-//
-// {predictData ? predictData.pay : "0"} -//
-//
-//
-//
Pay-raw:
-//
-// {predictData ? predictData.pay_raw : "0"} -//
-//
-//
-// -//
-//
-// ); -// }; +import React, { useMemo, useState } from "react"; +import { ITrade } from "@core/types"; +import { emptyPredict, fromGroths, setDataRequest, toGroths } from "@core/appUtils"; +import { AssetsContainer, AssetsSection, Button, Input, Section, Title, Window, Container } from "@app/shared/components"; +import { useInput } from "@app/shared/hooks"; +import * as mainActions from "@app/containers/Pools/store/actions"; +import { useDispatch, useSelector } from "react-redux"; +import "./index.scss"; +import { + selectCurrentPool, + selectPredirect, +} from "@app/containers/Pools/store/selectors"; +import { titleSections } from "@app/shared/constants"; + + +export const WithdrawPool = () => { + const data = useSelector(selectCurrentPool()); + const predictData = useSelector(selectPredirect()); + const amountInput = useInput(0); + const [requestData, setRequestData] = useState(null); + const dispatch = useDispatch(); + + + useMemo(() => { + setRequestData({ + aid1: data.aid1, + aid2: data.aid2, + kind: data.kind, + ctl: toGroths(Number(amountInput.value)), + }); + }, [amountInput.value]); + + useMemo(() => { + if (amountInput.value !== 0) { + dispatch(mainActions.onWithdraw.request(requestData)); + } + }, [requestData, amountInput.value]); + + const onWithdraw = (data: ITrade) => { + dispatch(mainActions.onWithdraw.request(setDataRequest(data))); + }; + return ( + + + +
+ + amountInput.onChange(e)} + /> +

{"AMML"}

+
+
+
+
+ + +

{data.metadata1.N}

+
+ + +

{data.metadata2.N}

+
+
+ +
+ +
+
+
+ ); +}; diff --git a/src/app/containers/Pools/interfaces/DexStateType.ts b/src/app/containers/Pools/interfaces/DexStateType.ts index 4f85a25..18fe11e 100644 --- a/src/app/containers/Pools/interfaces/DexStateType.ts +++ b/src/app/containers/Pools/interfaces/DexStateType.ts @@ -1,4 +1,4 @@ -import {IAsset, IPoolCard, IPredict, ITxStatus} from "@core/types"; +import { IAsset, IOptions, IPoolCard, IPredict, ITxStatus } from "@core/types"; export interface DexStateType { @@ -8,5 +8,7 @@ export interface DexStateType { statusTransaction: number | null, predict: IPredict, currentPool: IPoolCard, - filter: string + filter: string, + + options: IOptions[]; } diff --git a/src/app/containers/Pools/store/actions.ts b/src/app/containers/Pools/store/actions.ts index 86ab62f..60b742b 100644 --- a/src/app/containers/Pools/store/actions.ts +++ b/src/app/containers/Pools/store/actions.ts @@ -1,5 +1,15 @@ import {createAction, createAsyncAction} from "typesafe-actions"; -import {IAddLiquidity, IAsset, ICreatePool, IPoolCard, IPredict, ITrade, ITxStatus, IWithdraw} from "@core/types"; +import { + IAddLiquidity, + IAsset, + ICreatePool, + IOptions, + IPoolCard, + IPredict, + ITrade, + ITxStatus, + IWithdraw +} from "@core/types"; import {MainActionsTypes} from "@app/containers/Pools/store/constants"; export const setAssetsList = createAction(MainActionsTypes.SET_ASSETS_LIST)(); @@ -10,8 +20,7 @@ export const setTransactionStatus = createAction(MainActionsTypes.SET_TRANSACTIO export const setPredict = createAction(MainActionsTypes.SET_PREDICT)(); export const setCurrentPool = createAction(MainActionsTypes.SET_CURRENT_POOL)(); export const setFilter = createAction(MainActionsTypes.SET_FILTER)(); - - +export const setOptions = createAction(MainActionsTypes.SET_OPTIONS)(); export const loadAppParams = createAsyncAction( MainActionsTypes.LOAD_PARAMS, MainActionsTypes.LOAD_PARAMS_SUCCESS, diff --git a/src/app/containers/Pools/store/constants.ts b/src/app/containers/Pools/store/constants.ts index c59d13a..a67ecda 100644 --- a/src/app/containers/Pools/store/constants.ts +++ b/src/app/containers/Pools/store/constants.ts @@ -8,6 +8,7 @@ export enum MainActionsTypes{ SET_PREDICT = 'SET_PREDICT', SET_CURRENT_POOL = '@@MAIN/SET_CURRENT_POOL', SET_FILTER = 'SET_FILTER', + SET_OPTIONS ="SET_OPTIONS", LOAD_PARAMS = '@@MAIN/LOAD_PARAMS', LOAD_PARAMS_SUCCESS = '@@MAIN/LOAD_PARAMS_SUCCESS', diff --git a/src/app/containers/Pools/store/reducer.ts b/src/app/containers/Pools/store/reducer.ts index b7d9be3..6296923 100644 --- a/src/app/containers/Pools/store/reducer.ts +++ b/src/app/containers/Pools/store/reducer.ts @@ -12,7 +12,8 @@ const initialState: DexStateType = { statusTransaction: null, predict: null, currentPool: null, - filter: 'all' + filter: 'liquid', + options: [], }; @@ -38,6 +39,9 @@ const reducer = createReducer(initialState) .handleAction(actions.setFilter, (state, action) => produce(state, (nexState) => { nexState.filter = action.payload })) + .handleAction(actions.setOptions, (state, action) => produce(state, (nexState) => { + nexState.options = action.payload + })) export { reducer as MainReducer }; diff --git a/src/app/containers/Pools/store/saga.ts b/src/app/containers/Pools/store/saga.ts index 2a6f4d9..7e4ffc9 100644 --- a/src/app/containers/Pools/store/saga.ts +++ b/src/app/containers/Pools/store/saga.ts @@ -2,7 +2,7 @@ import {call, delay, put, select, takeLatest} from 'redux-saga/effects'; import {IAsset, IPoolCard, ITxId, ITxResult, ITxStatus, TxStatus} from "@core/types"; import {actions} from '.'; import {AddLiquidityApi, CreatePoolApi, LoadAssetsList, LoadPoolsList, TradePoolApi, WithdrawApi} from "@core/api"; -import { checkTxStatus, onFilter, parseMetadata, parsePoolMetadata } from "@core/appUtils"; +import { checkTxStatus, getOptions, onFilter, parseMetadata, parsePoolMetadata } from "@core/appUtils"; import {AppState} from "@app/shared/interface"; import * as mainActions from "@app/containers/Pools/store/actions"; import { selectFilter } from "@app/containers/Pools/store/selectors"; @@ -14,10 +14,12 @@ export function* loadParamsSaga( try { const filter = yield select(selectFilter()) const assetsList = (yield call(LoadAssetsList, action.payload ? action.payload : null)) as IAsset[] - assetsList.forEach((asset) => { + assetsList.forEach((asset) => { asset['parsedMetadata'] = parseMetadata(asset.metadata); }); yield put(mainActions.setAssetsList(assetsList)); + const options = getOptions(assetsList) + yield put(mainActions.setOptions(options)) const poolsList = (yield call(LoadPoolsList, action.payload? action.payload : null)) as IPoolCard[]; const newPoolList = poolsList.map((pool)=>{ return parsePoolMetadata(pool,pool.aid1, pool.aid2, assetsList) diff --git a/src/app/containers/Pools/store/selectors.ts b/src/app/containers/Pools/store/selectors.ts index 3cc2ba9..dea417a 100644 --- a/src/app/containers/Pools/store/selectors.ts +++ b/src/app/containers/Pools/store/selectors.ts @@ -10,3 +10,4 @@ export const selectTxStatus = () => createSelector(selectMain, (state) => state. export const selectPredirect = () => createSelector(selectMain, (state) => state.predict); export const selectCurrentPool = () => createSelector(selectMain, (state) => state.currentPool); export const selectFilter= () => createSelector(selectMain, (state) => state.filter); +export const selectOptions= () => createSelector(selectMain, (state) => state.options); diff --git a/src/app/core/appUtils.ts b/src/app/core/appUtils.ts index b191256..a3818d8 100644 --- a/src/app/core/appUtils.ts +++ b/src/app/core/appUtils.ts @@ -1,4 +1,14 @@ -import { IAddLiquidity, IAsset, ICreatePool, IPoolCard, IPredict, ITrade, ITxStatus, Kind } from "@core/types"; +import { + IAddLiquidity, + IAsset, + ICreatePool, + IOptions, + IPoolCard, + IPredict, + ITrade, + ITxStatus, + Kind +} from "@core/types"; import {ASSET_BEAM, GROTHS_IN_BEAM} from "@app/shared/constants"; export function parseMetadata(metadata) { @@ -11,6 +21,17 @@ export function parseMetadata(metadata) { return obj; } +export function getOptions(assets: IAsset[]) { + let options = [{ + value: 0, + label: `0 BEAM` + }] + assets.map(item => { + options = [...options, { value: item.aid, label:`${item.aid} ${item.parsedMetadata.N}`}] + }) + return options +} + export const getPoolKind = (kind: number) => { let kindDesc = null; @@ -91,7 +112,7 @@ export function onFilter(data: IPoolCard[], filter, assetsList:IAsset[]){ return data.filter(el=>!el.ctl) } default: - return data + return data.filter(el=>el.ctl) } } @@ -117,5 +138,21 @@ export const onPredictValue = (value, swap:boolean, predict: IPredict) => { return fromGroths(predict.tok2) } +export const getFilterPools = (filtered: IOptions[], data:IPoolCard[]):IPoolCard[] => { + if (filtered.length !== 0) { + let newList = [] + filtered.map(el => { + data.map(item => { + if (item.aid1 == el.value || item.aid2 === el.value) { + return newList = [...newList, item] + } + }) + console.log(newList) + return newList + }) + return newList + }else return data + } + diff --git a/src/app/core/types.ts b/src/app/core/types.ts index a8ea793..a546ad8 100644 --- a/src/app/core/types.ts +++ b/src/app/core/types.ts @@ -1,6 +1,6 @@ export type Pallete = 'green' | 'ghost' | 'purple' | 'blue' | 'red' | 'white' | 'vote-red'; -export type ButtonVariant = 'regular' | 'ghost' | 'ghostBordered' | 'block' | 'link' | 'icon'; +export type ButtonVariant = 'regular' | 'ghost' | 'ghostBordered' | 'block' | 'link' | 'icon' | 'control'; export enum Kind { Low = 0, @@ -122,8 +122,6 @@ export interface ITxResult { txid?: string, res?: IPredict } - - export interface IError { answer: { id: string, @@ -136,3 +134,8 @@ export interface IError { error: string; } + +export interface IOptions { + value: number, + label: string +} diff --git a/src/app/shared/components/AssetsContainer.tsx b/src/app/shared/components/AssetsContainer.tsx index 73ea0e4..8936a02 100644 --- a/src/app/shared/components/AssetsContainer.tsx +++ b/src/app/shared/components/AssetsContainer.tsx @@ -1,20 +1,22 @@ import React from "react"; import { styled } from "@linaria/react"; interface SectionProps { - variant?: 'column' + variant?: 'center' | 'space-between' } const SectionStyled = styled.div` display: flex; - justify-content: space-between; + justify-content: ${({variant}) => variant}; max-width: 954px; width: 100%; ` + const AssetsContainer: React.FC = ({ - children + children, + variant= 'space-between' }) => { return ( - + {children} ); diff --git a/src/app/shared/components/BackButton.tsx b/src/app/shared/components/BackButton.tsx index 702d148..e8b0d48 100644 --- a/src/app/shared/components/BackButton.tsx +++ b/src/app/shared/components/BackButton.tsx @@ -1,24 +1,31 @@ import React from 'react'; import { css, cx } from '@linaria/core'; -import { AngleBackIcon } from '@app/shared/icons'; +import { AngleBackIcon, ArrowLeftIcon } from "@app/shared/icons"; import Button from './Button'; interface BackButtonProps { className?: string; onClick: React.MouseEventHandler; + title: string } const backStyle = css` position: fixed; z-index: 3; - top: 73px; left: 15px; `; +const title = css` + color:white; + margin-left: 10px; +` -const BackButton: React.FC = ({ className, onClick }) => ( - ); export default BackButton; diff --git a/src/app/shared/components/Button.tsx b/src/app/shared/components/Button.tsx index 9bd5919..8655fa4 100644 --- a/src/app/shared/components/Button.tsx +++ b/src/app/shared/components/Button.tsx @@ -9,6 +9,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes { } const BaseButtonStyled = styled.button` + font-family: SFProDisplay; &[disabled] { opacity: 0.5; @@ -23,16 +24,13 @@ const BaseButtonStyled = styled.button` const ButtonStyled = styled(BaseButtonStyled)` display: block; width: 100%; - max-width: 254px; - margin: 0 auto; - margin-bottom: 10px; - padding: 12px 24px; + height: 37px; border: none; - border-radius: 22px; + border-radius: 19px; background-color: ${({ pallete }) => `var(--color-${pallete})`}; text-align: center; - font-weight: bold; - font-size: 16px; + font-weight: 700; + font-size: 14px; color: var(--color-dark-blue); &:hover, @@ -99,6 +97,17 @@ const LinkButtonStyled = styled(IconButtonStyled)` font-weight: 700; color: ${({ pallete }) => `var(--color-${pallete})`}; `; +const ControlButton = styled(IconButtonStyled)` + font-style: normal; + font-weight: 700; + font-size: 14px; + line-height: 17px; + text-transform: lowercase; + svg{ + margin-right: 16px; + vertical-align: middle; + } +` const VARIANTS = { regular: ButtonStyled, @@ -106,6 +115,8 @@ const VARIANTS = { link: LinkButtonStyled, icon: IconButtonStyled, block: BlockButtonStyled, + control: ControlButton + }; const Button: React.FC = ({ diff --git a/src/app/shared/components/Container.tsx b/src/app/shared/components/Container.tsx index 4f7ac3c..5bca6af 100644 --- a/src/app/shared/components/Container.tsx +++ b/src/app/shared/components/Container.tsx @@ -5,11 +5,12 @@ const ContainerStyled = styled.div` display: flex; flex-direction: column; align-items: center; - margin: 50px 0; + margin: 30px 0; width: 100%; min-height: 600px; height: 100%; justify-content: space-between; + margin-left: 5px; `; const Container: React.FC = ({ diff --git a/src/app/shared/components/ReactSelect.tsx b/src/app/shared/components/ReactSelect.tsx new file mode 100644 index 0000000..a0b0806 --- /dev/null +++ b/src/app/shared/components/ReactSelect.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import Select from "react-select"; +import { IOptions } from "@core/types"; + +interface IReactSelectProps { + options: IOptions[], + onChange: (any)=>void, + isFilter: boolean, +} + +const ReactSelect = ({options, onChange, isFilter, ...rest}:IReactSelectProps) => { + return ( + amountInput_aid1.onChange(e)} - /> -
- {tokenName_1} -
- - -
- - amountInput_aid2.onChange(e)} - /> -
- {tokenName_2} -
-
-
-
): ( - + {isEmpty ? ( + + {/* Select Pair */} +
+ + amountInput_aid1.onChange(e)} + /> + + +
+
+ + amountInput_aid2.onChange(e)} + /> + + +
+
+ ) : ( +
currentInput.onChange(e)} /> -

{isSwap ? tokenName_2 : tokenName_1}

+
- +
-

{isSwap ? tokenName_1 : tokenName_2}

+
)} - {/*// Create components for predirect*/} + {/* // Create components for predirect */}
-
{tokenName_1}:
+
+ {tokenName_1} + : +
+ {/* TODO: Create components */}
{!predictData || currentInput.value == 0 ? 0 : fromGroths(predictData.tok1)}
-
{tokenName_2}:
+
+ {tokenName_2} + : +
{!predictData || currentInput.value == 0 ? 0 : fromGroths(predictData.tok2)}
diff --git a/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx b/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx index af2bdaa..a605095 100644 --- a/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx +++ b/src/app/containers/Pools/containers/CreatePool/CreatePool.tsx @@ -1,19 +1,17 @@ -import React, {useCallback, useEffect, useState} from "react"; -import "./index.scss"; -import { AssetsContainer, Button, Section, Title, Window, Container } from "@app/shared/components"; -import {useDispatch, useSelector} from "react-redux"; +import React, { + useEffect, useMemo, useState, +} from 'react'; +import './index.scss'; +import { + AssetsContainer, Button, Section, Window, Container, ReactSelect, +} from '@app/shared/components'; +import { useDispatch, useSelector } from 'react-redux'; import { - selectAssetsList, selectOptions, - selectPoolsList, - selectTxStatus -} from "@app/containers/Pools/store/selectors"; -import Select from 'react-select' -import { ICreatePool, TxStatus} from "@core/types"; -import { kindSelect, ROUTES_PATH, titleSections } from "@app/shared/constants"; -import {useNavigate} from "react-router-dom"; -import * as mainActions from "@app/containers/Pools/store/actions"; -import { styled } from "@linaria/react"; +} from '@app/containers/Pools/store/selectors'; +import { ICreatePool } from '@core/types'; +import { kindSelect, titleSections } from '@app/shared/constants'; +import * as mainActions from '@app/containers/Pools/store/actions'; // const Container = styled.div` // display: flex; @@ -26,122 +24,102 @@ import { styled } from "@linaria/react"; // justify-content: space-between; // `; - export const CreatePool = () => { - const assetsList = useSelector(selectAssetsList()); - const poolsList = useSelector(selectPoolsList()); - const txStatus = useSelector(selectTxStatus()); - const options = useSelector(selectOptions()) - const [options2pair, setOptions2Pair] = useState([]) + const options = useSelector(selectOptions()); + const [options2pair, setOptions2Pair] = useState([]); + const [requestData, setRequestData] = useState(null); const [currentToken1, setCurrentToken1] = useState(null); const [currentToken2, setCurrentToken2] = useState(null); const [currentKind, setCurrentKind] = useState(2); const [isValidate, setIsValidate] = useState(false); - const navigate = useNavigate() - const dispatch = useDispatch() - - const requestData: ICreatePool[] = [{ - aid1: currentToken1, - aid2 :currentToken2, - kind: currentKind - }] + const dispatch = useDispatch(); + useEffect(() => { + setRequestData({ + aid1: currentToken1, + aid2: currentToken2, + kind: currentKind, + }); + }, [currentToken1, currentToken2, currentKind]); - const addLiquidityNavigation = useCallback((data) => { - dispatch(mainActions.setCurrentPool(data)) - navigate(ROUTES_PATH.POOLS.ADD_LIQUIDITY); - }, [navigate]); - - useEffect(()=>{ - if(txStatus && txStatus === TxStatus.Completed){ - let newPool - poolsList.filter(item => { - if(item.aid1 === requestData[0].aid1 && item.aid2 === requestData[0].aid2 && item.kind === requestData[0].kind){ - newPool = item - } - }) - addLiquidityNavigation(newPool) - } - },[txStatus]) - const getOptionsSecondPare = (lists, value: number) =>{ - if(lists && value || value === 0){ - setOptions2Pair(lists.filter((item) => item.value > value)) + // useEffect(() => { + // if (txStatus && txStatus === TxStatus.Completed) { + // let newPool; + // poolsList.filter((item) => { + // if ( + // item.aid1 === requestData[0].aid1 + // && item.aid2 === requestData[0].aid2 + // && item.kind === requestData[0].kind + // ) { + // newPool = item; + // } + // }); + // } + // }, [txStatus]); + const getOptionsSecondPare = (lists, value: number) => { + if ((lists && value) || value === 0) { + setOptions2Pair(lists.filter((item) => item.value > value)); } - return lists - } - - const getKindValue = () => { - return kindSelect.find((elem) => elem.value === currentKind); - } + return lists; + }; + useMemo(() => { + if (currentToken1 === null) { + setIsValidate(false); + } else if (currentToken2 === null) { + setIsValidate(false); + } else if (currentKind === null) { + setIsValidate(false); + } else setIsValidate(true); + }, [currentToken1, currentToken2, currentKind]); - useEffect(()=>{ - getOptionsSecondPare(options, currentToken1) - },[ currentToken1]) + const getKindValue = () => kindSelect.find((elem) => elem.value === currentKind); - const onChangeToken1 = ( newValue) => { - setCurrentToken1(newValue.value) - } - const onChangeToken2 = (newValue ) => { - setCurrentToken2(newValue.value) - } - const onChangeKind = ( newValue) => { - setCurrentKind(newValue.value) - } + useEffect(() => { + getOptionsSecondPare(options, currentToken1); + }, [currentToken1]); - const checkValidate = () => { - requestData.map(item=> { - if(item.aid1 !== null && item.aid2 !== null && item.kind !== null){ - return setIsValidate(true) - } - setIsValidate(false) - }) - } - useEffect(()=>{ - checkValidate() - },[requestData]) + const onChangeToken1 = (newValue) => { + setCurrentToken1(newValue.value); + }; + const onChangeToken2 = (newValue) => { + setCurrentToken2(newValue.value); + }; + const onChangeKind = (newValue) => { + setCurrentKind(newValue.value); + }; - const onCreatePool = (data: ICreatePool[]) => { - dispatch(mainActions.onCreatePool.request(data)) - } + const onCreatePool = (data: ICreatePool[]) => { + dispatch(mainActions.onCreatePool.request(data)); + }; return ( - +
- +
- Fee tier indicates the liquidity of the pool assets. It is - recommended to use low tier for stable assets only. + Fee tier indicates the liquidity of the pool assets. It is recommended to use low tier for stable assets + only.
- amountInput.onChange(e)} - /> -

{isSwap ? tokenName_2 :tokenName_1}

- -
- -
- - -

{isSwap ? tokenName_1 :tokenName_2}

-
+ return ( + + + +
+ + amountInput.onChange(e)} + /> + + +
+ +
+ + + + +
+
+
+
+
+ {data.metadata1.N} + : +
+
{data.tok1}
+
+
+
+ {data.metadata2.N} + : +
+
{data.tok2}
+
+
+
Buy:
+
{predictData ? predictData.buy : '0'}
+
+
+
Fee-dao:
+
{predictData ? predictData.fee_dao : '0'}
+
+
+
Fee-pool:
+
{predictData ? predictData.fee_pool : '0'}
+
+
+
Pay:
+
{predictData ? predictData.pay : '0'}
+
+
+
Pay-raw:
+
{predictData ? predictData.pay_raw : '0'}
+
- -
-
-
{data.metadata1.N}:
-
{data.tok1}
-
-
-
{data.metadata2.N}:
-
{data.tok2}
-
-
-
Buy:
-
- {predictData ? predictData.buy : "0"} -
-
-
-
Fee-dao:
-
- {predictData ? predictData.fee_dao : "0"} -
-
-
-
Fee-pool:
-
- {predictData ? predictData.fee_pool : "0"} -
-
-
-
Pay:
-
- {predictData ? predictData.pay : "0"} -
-
-
-
Pay-raw:
-
- {predictData ? predictData.pay_raw : "0"} -
-
-
-
- -
-
-
+
+ +
+ + ); }; diff --git a/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx b/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx index 5b5577c..44112c2 100644 --- a/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx +++ b/src/app/containers/Pools/containers/WithdrawPool/WithdrawPool.tsx @@ -1,17 +1,20 @@ -import React, { useMemo, useState } from "react"; -import { ITrade } from "@core/types"; -import { emptyPredict, fromGroths, setDataRequest, toGroths } from "@core/appUtils"; -import { AssetsContainer, AssetsSection, Button, Input, Section, Title, Window, Container } from "@app/shared/components"; -import { useInput } from "@app/shared/hooks"; -import * as mainActions from "@app/containers/Pools/store/actions"; -import { useDispatch, useSelector } from "react-redux"; -import "./index.scss"; +import React, { useMemo, useState } from 'react'; +import { ITrade } from '@core/types'; +import { + emptyPredict, fromGroths, setDataRequest, toGroths, +} from '@core/appUtils'; +import { + AssetsContainer, AssetsSection, Button, Input, Section, Title, Window, Container, +} from '@app/shared/components'; +import { useInput } from '@app/shared/hooks'; +import * as mainActions from '@app/containers/Pools/store/actions'; +import { useDispatch, useSelector } from 'react-redux'; +import './index.scss'; import { selectCurrentPool, selectPredirect, -} from "@app/containers/Pools/store/selectors"; -import { titleSections } from "@app/shared/constants"; - +} from '@app/containers/Pools/store/selectors'; +import { titleSections } from '@app/shared/constants'; export const WithdrawPool = () => { const data = useSelector(selectCurrentPool()); @@ -20,7 +23,6 @@ export const WithdrawPool = () => { const [requestData, setRequestData] = useState(null); const dispatch = useDispatch(); - useMemo(() => { setRequestData({ aid1: data.aid1, @@ -40,19 +42,19 @@ export const WithdrawPool = () => { dispatch(mainActions.onWithdraw.request(setDataRequest(data))); }; return ( - + - +
amountInput.onChange(e)} /> -

{"AMML"}

+

AMML

@@ -61,10 +63,10 @@ export const WithdrawPool = () => {

{data.metadata1.N}

@@ -72,10 +74,10 @@ export const WithdrawPool = () => {

{data.metadata2.N}

diff --git a/src/app/containers/Pools/interfaces/DexStateType.ts b/src/app/containers/Pools/interfaces/DexStateType.ts index 18fe11e..3281018 100644 --- a/src/app/containers/Pools/interfaces/DexStateType.ts +++ b/src/app/containers/Pools/interfaces/DexStateType.ts @@ -1,14 +1,15 @@ -import { IAsset, IOptions, IPoolCard, IPredict, ITxStatus } from "@core/types"; - +import { + IAsset, IOptions, IPoolCard, IPredict, ITxStatus, +} from '@core/types'; export interface DexStateType { - assetsList: IAsset[]; - poolsList: IPoolCard[]; - tx_status: ITxStatus[] | null, - statusTransaction: number | null, - predict: IPredict, - currentPool: IPoolCard, - filter: string, + assetsList: IAsset[]; + poolsList: IPoolCard[]; + tx_status: ITxStatus[] | null, + statusTransaction: number | null, + predict: IPredict, + currentPool: IPoolCard, + filter: string, - options: IOptions[]; + options: IOptions[]; } diff --git a/src/app/containers/Pools/store/actions.ts b/src/app/containers/Pools/store/actions.ts index 60b742b..d517ce3 100644 --- a/src/app/containers/Pools/store/actions.ts +++ b/src/app/containers/Pools/store/actions.ts @@ -1,4 +1,4 @@ -import {createAction, createAsyncAction} from "typesafe-actions"; +import { createAction, createAsyncAction } from 'typesafe-actions'; import { IAddLiquidity, IAsset, @@ -8,49 +8,48 @@ import { IPredict, ITrade, ITxStatus, - IWithdraw -} from "@core/types"; -import {MainActionsTypes} from "@app/containers/Pools/store/constants"; + IWithdraw, +} from '@core/types'; +import { MainActionsTypes } from '@app/containers/Pools/store/constants'; export const setAssetsList = createAction(MainActionsTypes.SET_ASSETS_LIST)(); export const setPoolsList = createAction(MainActionsTypes.SET_POOLS_LIST)(); export const setTxStatus = createAction(MainActionsTypes.SET_TX_STATUS)(); export const setErrorMessage = createAction(MainActionsTypes.SET_ERROR_MESSAGE)(); -export const setTransactionStatus = createAction(MainActionsTypes.SET_TRANSACTIONS_STATUS)(); +export const setTransactionStatus = createAction(MainActionsTypes.SET_TRANSACTIONS_STATUS)(); export const setPredict = createAction(MainActionsTypes.SET_PREDICT)(); export const setCurrentPool = createAction(MainActionsTypes.SET_CURRENT_POOL)(); export const setFilter = createAction(MainActionsTypes.SET_FILTER)(); export const setOptions = createAction(MainActionsTypes.SET_OPTIONS)(); export const loadAppParams = createAsyncAction( - MainActionsTypes.LOAD_PARAMS, - MainActionsTypes.LOAD_PARAMS_SUCCESS, - MainActionsTypes.LOAD_PARAMS_FAILURE + MainActionsTypes.LOAD_PARAMS, + MainActionsTypes.LOAD_PARAMS_SUCCESS, + MainActionsTypes.LOAD_PARAMS_FAILURE, )(); export const loadPoolsList = createAsyncAction( - MainActionsTypes.LOAD_POOLS_LIST, - MainActionsTypes.LOAD_POOLS_LIST_SUCCESS, - MainActionsTypes.LOAD_POOLS_LIST_FAILURE, + MainActionsTypes.LOAD_POOLS_LIST, + MainActionsTypes.LOAD_POOLS_LIST_SUCCESS, + MainActionsTypes.LOAD_POOLS_LIST_FAILURE, )(); export const onCreatePool = createAsyncAction( - MainActionsTypes.CREATE_POOL, - MainActionsTypes.CREATE_POOL_SUCCESS, - MainActionsTypes.CREATE_POOL_FAILURE, + MainActionsTypes.CREATE_POOL, + MainActionsTypes.CREATE_POOL_SUCCESS, + MainActionsTypes.CREATE_POOL_FAILURE, )(); export const onAddLiquidity = createAsyncAction( - MainActionsTypes.ADD_LIQUIDITY, - MainActionsTypes.ADD_LIQUIDITY_SUCCESS, - MainActionsTypes.ADD_LIQUIDITY_FAILURE, + MainActionsTypes.ADD_LIQUIDITY, + MainActionsTypes.ADD_LIQUIDITY_SUCCESS, + MainActionsTypes.ADD_LIQUIDITY_FAILURE, )(); export const onTradePool = createAsyncAction( - MainActionsTypes.TRADE_POOL, - MainActionsTypes.TRADE_POOL_SUCCESS, - MainActionsTypes.TRADE_POOL_FAILURE, + MainActionsTypes.TRADE_POOL, + MainActionsTypes.TRADE_POOL_SUCCESS, + MainActionsTypes.TRADE_POOL_FAILURE, )(); export const onWithdraw = createAsyncAction( - MainActionsTypes.WITHDRAW, - MainActionsTypes.WITHDRAW_SUCCESS, - MainActionsTypes.WITHDRAW_FAILURE, + MainActionsTypes.WITHDRAW, + MainActionsTypes.WITHDRAW_SUCCESS, + MainActionsTypes.WITHDRAW_FAILURE, )(); - diff --git a/src/app/containers/Pools/store/constants.ts b/src/app/containers/Pools/store/constants.ts index a67ecda..aeedb5e 100644 --- a/src/app/containers/Pools/store/constants.ts +++ b/src/app/containers/Pools/store/constants.ts @@ -1,36 +1,34 @@ export enum MainActionsTypes{ - SET_ASSETS_LIST = '@@MAIN/SET_ASSETS_LIST', - SET_POOLS_LIST = '@@MAIN/SET_POOLS_LIST', - SET_TX_STATUS = '@@MAIN/SET_TX_STATUS', + SET_ASSETS_LIST = '@@MAIN/SET_ASSETS_LIST', + SET_POOLS_LIST = '@@MAIN/SET_POOLS_LIST', + SET_TX_STATUS = '@@MAIN/SET_TX_STATUS', - SET_TRANSACTIONS_STATUS= 'SET_TRANSACTIONS_STATUS', - SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE', - SET_PREDICT = 'SET_PREDICT', - SET_CURRENT_POOL = '@@MAIN/SET_CURRENT_POOL', - SET_FILTER = 'SET_FILTER', - SET_OPTIONS ="SET_OPTIONS", + SET_TRANSACTIONS_STATUS = 'SET_TRANSACTIONS_STATUS', + SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE', + SET_PREDICT = 'SET_PREDICT', + SET_CURRENT_POOL = '@@MAIN/SET_CURRENT_POOL', + SET_FILTER = 'SET_FILTER', + SET_OPTIONS = 'SET_OPTIONS', - LOAD_PARAMS = '@@MAIN/LOAD_PARAMS', - LOAD_PARAMS_SUCCESS = '@@MAIN/LOAD_PARAMS_SUCCESS', - LOAD_PARAMS_FAILURE = '@@MAIN/LOAD_PARAMS_FAILURE', + LOAD_PARAMS = '@@MAIN/LOAD_PARAMS', + LOAD_PARAMS_SUCCESS = '@@MAIN/LOAD_PARAMS_SUCCESS', + LOAD_PARAMS_FAILURE = '@@MAIN/LOAD_PARAMS_FAILURE', + LOAD_POOLS_LIST = '@@MAIN/LOAD_POOLS_LIST', + LOAD_POOLS_LIST_SUCCESS = '@@MAIN/LOAD_POOLS_LIST_SUCCESS', + LOAD_POOLS_LIST_FAILURE = '@@MAIN/LOAD_POOLS_LIST_FAILURE', - LOAD_POOLS_LIST = '@@MAIN/LOAD_POOLS_LIST', - LOAD_POOLS_LIST_SUCCESS = '@@MAIN/LOAD_POOLS_LIST_SUCCESS', - LOAD_POOLS_LIST_FAILURE = '@@MAIN/LOAD_POOLS_LIST_FAILURE', - - CREATE_POOL = '@@MAIN/CREATE_POOL', - CREATE_POOL_SUCCESS = '@@MAIN/CREATE_POOL_SUCCESS', - CREATE_POOL_FAILURE = '@@MAIN/CREATE_POOL_FAILURE', - ADD_LIQUIDITY = '@@MAIN/ADD_LIQUIDITY', - ADD_LIQUIDITY_SUCCESS = '@@MAIN/ADD_LIQUIDITY_SUCCESS', - ADD_LIQUIDITY_FAILURE = '@@MAIN/ADD_LIQUIDITY_FAILURE', - TRADE_POOL = '@@MAIN/TRADE_POOL', - TRADE_POOL_SUCCESS = '@@MAIN/TRADE_POOL_SUCCESS', - TRADE_POOL_FAILURE = '@@MAIN/TRADE_POOL_FAILURE', - WITHDRAW = '@@MAIN/WITHDRAW', - WITHDRAW_SUCCESS = '@@MAIN/WITHDRAW_SUCCESS', - WITHDRAW_FAILURE = '@@MAIN/WITHDRAW_FAILURE', - + CREATE_POOL = '@@MAIN/CREATE_POOL', + CREATE_POOL_SUCCESS = '@@MAIN/CREATE_POOL_SUCCESS', + CREATE_POOL_FAILURE = '@@MAIN/CREATE_POOL_FAILURE', + ADD_LIQUIDITY = '@@MAIN/ADD_LIQUIDITY', + ADD_LIQUIDITY_SUCCESS = '@@MAIN/ADD_LIQUIDITY_SUCCESS', + ADD_LIQUIDITY_FAILURE = '@@MAIN/ADD_LIQUIDITY_FAILURE', + TRADE_POOL = '@@MAIN/TRADE_POOL', + TRADE_POOL_SUCCESS = '@@MAIN/TRADE_POOL_SUCCESS', + TRADE_POOL_FAILURE = '@@MAIN/TRADE_POOL_FAILURE', + WITHDRAW = '@@MAIN/WITHDRAW', + WITHDRAW_SUCCESS = '@@MAIN/WITHDRAW_SUCCESS', + WITHDRAW_FAILURE = '@@MAIN/WITHDRAW_FAILURE', } diff --git a/src/app/containers/Pools/store/reducer.ts b/src/app/containers/Pools/store/reducer.ts index 6296923..4daf099 100644 --- a/src/app/containers/Pools/store/reducer.ts +++ b/src/app/containers/Pools/store/reducer.ts @@ -1,47 +1,45 @@ -import {DexStateType} from "@app/containers/Pools/interfaces/DexStateType"; -import {ActionType, createReducer} from "typesafe-actions"; +import { DexStateType } from '@app/containers/Pools/interfaces/DexStateType'; +import { ActionType, createReducer } from 'typesafe-actions'; +import produce from 'immer'; import * as actions from './actions'; -import produce from "immer"; type Action = ActionType; const initialState: DexStateType = { - assetsList: [], - poolsList: [], - tx_status: null, - statusTransaction: null, - predict: null, - currentPool: null, - filter: 'liquid', - options: [], + assetsList: [], + poolsList: [], + tx_status: null, + statusTransaction: null, + predict: null, + currentPool: null, + filter: 'liquid', + options: [], }; - const reducer = createReducer(initialState) - .handleAction(actions.setAssetsList, (state, action) => produce(state, (nexState) => { - nexState.assetsList = action.payload; - })) - .handleAction(actions.setPoolsList, (state, action) => produce(state, (nexState) => { - nexState.poolsList = action.payload; - })) - .handleAction(actions.setTxStatus, (state, action ) => produce(state, (nexState) => { - nexState.tx_status = action.payload - })) - .handleAction(actions.setTransactionStatus, (state, action ) => produce(state, (nexState) => { - nexState.statusTransaction = action.payload - })) - .handleAction(actions.setPredict, (state, action) => produce(state, (nexState) => { - nexState.predict = action.payload; - })) - .handleAction(actions.setCurrentPool, (state, action) => produce(state, (nexState) => { - nexState.currentPool = action.payload; - })) + .handleAction(actions.setAssetsList, (state, action) => produce(state, (nexState) => { + nexState.assetsList = action.payload; + })) + .handleAction(actions.setPoolsList, (state, action) => produce(state, (nexState) => { + nexState.poolsList = action.payload; + })) + .handleAction(actions.setTxStatus, (state, action) => produce(state, (nexState) => { + nexState.tx_status = action.payload; + })) + .handleAction(actions.setTransactionStatus, (state, action) => produce(state, (nexState) => { + nexState.statusTransaction = action.payload; + })) + .handleAction(actions.setPredict, (state, action) => produce(state, (nexState) => { + nexState.predict = action.payload; + })) + .handleAction(actions.setCurrentPool, (state, action) => produce(state, (nexState) => { + nexState.currentPool = action.payload; + })) .handleAction(actions.setFilter, (state, action) => produce(state, (nexState) => { - nexState.filter = action.payload - })) + nexState.filter = action.payload; + })) .handleAction(actions.setOptions, (state, action) => produce(state, (nexState) => { - nexState.options = action.payload - })) - + nexState.options = action.payload; + })); export { reducer as MainReducer }; diff --git a/src/app/containers/Pools/store/saga.ts b/src/app/containers/Pools/store/saga.ts index 7e4ffc9..497d872 100644 --- a/src/app/containers/Pools/store/saga.ts +++ b/src/app/containers/Pools/store/saga.ts @@ -1,150 +1,127 @@ -import {call, delay, put, select, takeLatest} from 'redux-saga/effects'; -import {IAsset, IPoolCard, ITxId, ITxResult, ITxStatus, TxStatus} from "@core/types"; -import {actions} from '.'; -import {AddLiquidityApi, CreatePoolApi, LoadAssetsList, LoadPoolsList, TradePoolApi, WithdrawApi} from "@core/api"; -import { checkTxStatus, getOptions, onFilter, parseMetadata, parsePoolMetadata } from "@core/appUtils"; -import {AppState} from "@app/shared/interface"; -import * as mainActions from "@app/containers/Pools/store/actions"; -import { selectFilter } from "@app/containers/Pools/store/selectors"; -import { toast } from "react-toastify"; - -export function* loadParamsSaga( - action: ReturnType, -): Generator { - try { - const filter = yield select(selectFilter()) - const assetsList = (yield call(LoadAssetsList, action.payload ? action.payload : null)) as IAsset[] - assetsList.forEach((asset) => { - asset['parsedMetadata'] = parseMetadata(asset.metadata); - }); - yield put(mainActions.setAssetsList(assetsList)); - const options = getOptions(assetsList) - yield put(mainActions.setOptions(options)) - const poolsList = (yield call(LoadPoolsList, action.payload? action.payload : null)) as IPoolCard[]; - const newPoolList = poolsList.map((pool)=>{ - return parsePoolMetadata(pool,pool.aid1, pool.aid2, assetsList) - }) - const filteredPools = (yield onFilter(newPoolList, filter, assetsList).map((pool)=>{ - return parsePoolMetadata(pool,pool.aid1, pool.aid2, assetsList) - })) as IPoolCard[] - yield put(mainActions.setPoolsList(filteredPools)); - - } - catch (e){ - // @ts-ignore - yield put(mainActions.loadAppParams.failure(e)); - toast(e.error) - } +import { + call, delay, put, select, takeLatest, +} from 'redux-saga/effects'; +import { + IAsset, IPoolCard, ITxId, ITxResult, ITxStatus, TxStatus, +} from '@core/types'; +import { + AddLiquidityApi, CreatePoolApi, LoadAssetsList, LoadPoolsList, TradePoolApi, WithdrawApi, +} from '@core/api'; +import { + checkTxStatus, getOptions, onFilter, parseMetadata, parsePoolMetadata, +} from '@core/appUtils'; +import { AppState } from '@app/shared/interface'; +import * as mainActions from '@app/containers/Pools/store/actions'; +import { selectFilter } from '@app/containers/Pools/store/selectors'; +import { toast } from 'react-toastify'; +import { actions } from '.'; + +export function* loadParamsSaga(action: ReturnType): Generator { + try { + const filter = yield select(selectFilter()); + const assetsList = (yield call(LoadAssetsList, action.payload ? action.payload : null)) as IAsset[]; + assetsList.forEach((asset) => { + asset.parsedMetadata = parseMetadata(asset.metadata); + }); + yield put(mainActions.setAssetsList(assetsList)); + const options = getOptions(assetsList); + yield put(mainActions.setOptions(options)); + const poolsList = (yield call(LoadPoolsList, action.payload ? action.payload : null)) as IPoolCard[]; + const newPoolList = poolsList.map((pool) => parsePoolMetadata(pool, pool.aid1, pool.aid2, assetsList)); + const filteredPools = (yield onFilter(newPoolList, filter).map((pool) => parsePoolMetadata(pool, pool.aid1, pool.aid2, assetsList))) as IPoolCard[]; + yield put(mainActions.setPoolsList(filteredPools)); + } catch (e) { + // @ts-ignore + yield put(mainActions.loadAppParams.failure(e)); + toast(e.error); + } } -function* getStatus (txid: string) { - const listStatus = yield select((state:AppState) => state.main.tx_status ) - console.log({txid, listStatus}) - const status = yield checkTxStatus(txid, listStatus as ITxStatus[]) - if(status === TxStatus.Completed) { - yield put(mainActions.setTransactionStatus(status)) - } else if (status === TxStatus.Failed) { - yield put(mainActions.setTransactionStatus(status)) - } - else if( status === TxStatus.Canceled) { - yield put(mainActions.setTransactionStatus(status)) - } else if (status === TxStatus.InProgress || TxStatus.Registering) { - console.log('in_prog') - yield put(mainActions.setTransactionStatus(status)) - yield delay(2000) - yield getStatus(txid) - } +function* getStatus(txid: string) { + const listStatus = yield select((state: AppState) => state.main.tx_status); + console.log({ txid, listStatus }); + const status = yield checkTxStatus(txid, listStatus as ITxStatus[]); + if (status === TxStatus.Completed) { + yield put(mainActions.setTransactionStatus(status)); + } else if (status === TxStatus.Failed) { + yield put(mainActions.setTransactionStatus(status)); + } else if (status === TxStatus.Canceled) { + yield put(mainActions.setTransactionStatus(status)); + } else if (status === TxStatus.InProgress || TxStatus.Registering) { + console.log('in_prog'); + yield put(mainActions.setTransactionStatus(status)); + yield delay(2000); + yield getStatus(txid); + } } -export function* createPool( - action: ReturnType, - -): Generator{ - try { - // @ts-ignore - const { txid } = (yield call(CreatePoolApi, action.payload ? action.payload : null)) as ITxId; - yield getStatus(txid) - } - catch (e) { - // @ts-ignore - yield put(mainActions.onCreatePool.failure(e)); - yield put(mainActions.setErrorMessage(e)); - } +export function* createPool(action: ReturnType): Generator { + try { + // @ts-ignore + const { txid } = (yield call(CreatePoolApi, action.payload ? action.payload : null)) as ITxId; + yield getStatus(txid); + } catch (e) { + // @ts-ignore + yield put(mainActions.onCreatePool.failure(e)); + yield put(mainActions.setErrorMessage(e)); + } } -export function* addLiquidity( - action: ReturnType, - -): Generator{ - try { - // @ts-ignore - const {txid, res} = (yield call(AddLiquidityApi, action.payload ? action.payload : null)) as ITxResult - if(res){ - yield put(mainActions.setPredict(res)) - } - if(txid){ - yield getStatus(txid) - } - +export function* addLiquidity(action: ReturnType): Generator { + try { + // @ts-ignore + const { txid, res } = (yield call(AddLiquidityApi, action.payload ? action.payload : null)) as ITxResult; + if (res) { + yield put(mainActions.setPredict(res)); } - catch (e) { - // @ts-ignore - yield put(mainActions.onAddLiquidity.failure(e)); - toast(e.error) - - - + if (txid) { + yield getStatus(txid); } + } catch (e) { + // @ts-ignore + yield put(mainActions.onAddLiquidity.failure(e)); + toast(e.error); + } } -export function* tradePool( - action: ReturnType, - -): Generator{ - try { - // @ts-ignore - const {res, txid } = (yield call(TradePoolApi, action.payload ? action.payload : null)) as ITxResult - if(res){ - yield put(mainActions.setPredict(res)) - } - if(txid){ - yield getStatus(txid) - } - +export function* tradePool(action: ReturnType): Generator { + try { + // @ts-ignore + const { res, txid } = (yield call(TradePoolApi, action.payload ? action.payload : null)) as ITxResult; + if (res) { + yield put(mainActions.setPredict(res)); } - catch (e) { - // @ts-ignore - yield put(mainActions.onTradePool.failure(e)); - yield put(mainActions.setErrorMessage(e)); - toast(e.error) + if (txid) { + yield getStatus(txid); } + } catch (e) { + // @ts-ignore + yield put(mainActions.onTradePool.failure(e)); + yield put(mainActions.setErrorMessage(e)); + toast(e.error); + } } -export function* withdrawPool( - action: ReturnType, - -): Generator{ - try { - // @ts-ignore - const {res, txid } = (yield call(WithdrawApi, action.payload ? action.payload : null)) as ITxResult - if(res){ - yield put(mainActions.setPredict(res)) - } - if(txid){ - yield getStatus(txid) - } - +export function* withdrawPool(action: ReturnType): Generator { + try { + // @ts-ignore + const { res, txid } = (yield call(WithdrawApi, action.payload ? action.payload : null)) as ITxResult; + if (res) { + yield put(mainActions.setPredict(res)); } - catch (e) { - // @ts-ignore - yield put(mainActions.onWithdraw.failure(e)); - toast(e.error) + if (txid) { + yield getStatus(txid); } + } catch (e) { + // @ts-ignore + yield put(mainActions.onWithdraw.failure(e)); + toast(e.error); + } } - function* mainSaga() { - yield takeLatest(mainActions.loadAppParams.request, loadParamsSaga); - yield takeLatest(mainActions.onCreatePool.request, createPool); - yield takeLatest(mainActions.onAddLiquidity.request, addLiquidity); - yield takeLatest(mainActions.onTradePool.request, tradePool); - yield takeLatest(mainActions.onWithdraw.request, withdrawPool); - } +function* mainSaga() { + yield takeLatest(mainActions.loadAppParams.request, loadParamsSaga); + yield takeLatest(mainActions.onCreatePool.request, createPool); + yield takeLatest(mainActions.onAddLiquidity.request, addLiquidity); + yield takeLatest(mainActions.onTradePool.request, tradePool); + yield takeLatest(mainActions.onWithdraw.request, withdrawPool); +} - export default mainSaga; +export default mainSaga; diff --git a/src/app/containers/Pools/store/selectors.ts b/src/app/containers/Pools/store/selectors.ts index dea417a..7ba20ad 100644 --- a/src/app/containers/Pools/store/selectors.ts +++ b/src/app/containers/Pools/store/selectors.ts @@ -1,6 +1,5 @@ import { createSelector } from 'reselect'; -import {AppState} from "@app/shared/interface"; - +import { AppState } from '@app/shared/interface'; const selectMain = (state: AppState) => state.main; @@ -9,5 +8,5 @@ export const selectPoolsList = () => createSelector(selectMain, (state) => state export const selectTxStatus = () => createSelector(selectMain, (state) => state.statusTransaction); export const selectPredirect = () => createSelector(selectMain, (state) => state.predict); export const selectCurrentPool = () => createSelector(selectMain, (state) => state.currentPool); -export const selectFilter= () => createSelector(selectMain, (state) => state.filter); -export const selectOptions= () => createSelector(selectMain, (state) => state.options); +export const selectFilter = () => createSelector(selectMain, (state) => state.filter); +export const selectOptions = () => createSelector(selectMain, (state) => state.options); diff --git a/src/app/core/api.tsx b/src/app/core/api.tsx index 30bd80f..4aa80b7 100644 --- a/src/app/core/api.tsx +++ b/src/app/core/api.tsx @@ -1,26 +1,26 @@ import Utils from '@core/utils.js'; import { - IAddLiquidity, ICreatePool, IError, ITrade, ITxId, ITxResult, IWithdraw, + IAddLiquidity, ICreatePool, IError, ITrade, ITxId, IWithdraw, } from '@core/types'; const CID = '4e0a28b2b2a83b811ad17ba8228b0645dbce2969fd453a68fbc0b60bc8860e02'; -const onMakeTx = (err, sres, full, params: { id: number; vote: number } = null, toasted: string = null) => { +const onMakeTx = (err, sres, full) => { if (err) { console.log(err, 'Failed to generate transaction request'); } - return new Promise((resolve, reject) => { - Utils.callApi('process_invoke_data', { data: full.result.raw_data }, (error, result, full) => { + return new Promise((resolve) => { + Utils.callApi('process_invoke_data', { data: full.result.raw_data }, (error, result) => { resolve(result); }); }); }; export function LoadAssetsList(payload): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { Utils.invokeContract( `action=view_all_assets,cid=${CID}`, - (error, result, full) => { + (error, result) => { resolve(result.res); }, payload || null, @@ -29,17 +29,17 @@ export function LoadAssetsList(payload): Promise { } export function LoadPoolsList(payload?): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { Utils.invokeContract( `action=pools_view,cid=${CID}`, - (error, result, full) => { + (error, result) => { resolve(result.res); }, payload || null, ); }); } -export function CreatePoolApi([{ aid1, aid2, kind }]: ICreatePool[]): Promise { +export function CreatePoolApi([{ aid1, aid2, kind }]: ICreatePool[]): Promise | any { return new Promise((resolve, reject) => { Utils.invokeContract( `action=pool_create,aid1=${aid1},aid2=${aid2},kind=${kind},cid=${CID}`, @@ -61,7 +61,7 @@ export function AddLiquidityApi({ val1, val2, bPredictOnly = 1, -}: IAddLiquidity): Promise { +}: IAddLiquidity): Promise | any { return new Promise((resolve, reject) => { Utils.invokeContract( `action=pool_add_liquidity,aid1=${aid1},aid2=${aid2},kind=${kind},val1=${val1},val2=${val2},bPredictOnly=${bPredictOnly},cid=${CID}`, @@ -81,7 +81,7 @@ export function AddLiquidityApi({ } export function TradePoolApi({ aid1, aid2, kind, val1_buy, bPredictOnly = 1, -}: ITrade): Promise { +}: ITrade): Promise | any { return new Promise((resolve, reject) => { Utils.invokeContract( `action=pool_trade,aid1=${aid1},aid2=${aid2},kind=${kind},val1_buy=${val1_buy},bPredictOnly=${bPredictOnly},cid=${CID}`, @@ -102,7 +102,7 @@ export function TradePoolApi({ export function WithdrawApi({ aid1, aid2, kind, ctl, bPredictOnly = 1, -}: IWithdraw): Promise { +}: IWithdraw): Promise | any { return new Promise((resolve, reject) => { Utils.invokeContract( `action=pool_withdraw,aid1=${aid1},aid2=${aid2},kind=${kind},ctl=${ctl},bPredictOnly=${bPredictOnly},cid=${CID}`, @@ -120,3 +120,4 @@ export function WithdrawApi({ ); }); } +// TODO: ADD TYPE diff --git a/src/app/core/appUtils.ts b/src/app/core/appUtils.ts index a3818d8..4f4b0ae 100644 --- a/src/app/core/appUtils.ts +++ b/src/app/core/appUtils.ts @@ -1,158 +1,156 @@ import { - IAddLiquidity, - IAsset, - ICreatePool, - IOptions, - IPoolCard, - IPredict, - ITrade, - ITxStatus, - Kind -} from "@core/types"; -import {ASSET_BEAM, GROTHS_IN_BEAM} from "@app/shared/constants"; + IAsset, + IOptions, + IPoolCard, + IPredict, + ITxStatus, + Kind, +} from '@core/types'; +import { ASSET_BEAM, GROTHS_IN_BEAM } from '@app/shared/constants'; export function parseMetadata(metadata) { - const splittedMetadata = metadata.split(';'); - splittedMetadata.shift(); - const obj = splittedMetadata.reduce((accumulator, value, index) => { - const data = value.split(/=(.*)/s); - return {...accumulator, [data[0]]: data[1]}; - }, {}); - return obj; + const splittedMetadata = metadata.split(';'); + splittedMetadata.shift(); + const obj = splittedMetadata.reduce((accumulator, value) => { + const data = value.split(/=(.*)/s); + return { ...accumulator, [data[0]]: data[1] }; + }, {}); + return obj; } export function getOptions(assets: IAsset[]) { - let options = [{ - value: 0, - label: `0 BEAM` - }] - assets.map(item => { - options = [...options, { value: item.aid, label:`${item.aid} ${item.parsedMetadata.N}`}] - }) - return options + let options = [{ + value: 0, + label: '0 BEAM', + }]; + + assets.map((item) => { + options = [...options, { value: item.aid, label: `${item.aid} ${item.parsedMetadata.UN}` }]; + return options; + }); + return options; } export const getPoolKind = (kind: number) => { - let kindDesc = null; + let kindDesc = null; - if(kind === Kind.Low) - kindDesc = "0.05%"; + if (kind === Kind.Low) { kindDesc = '0.05%'; } - if(kind === Kind.Mid) - kindDesc = "0.3%"; + if (kind === Kind.Mid) { kindDesc = '0.3%'; } - if(kind === Kind.High) - kindDesc = "1%"; + if (kind === Kind.High) { kindDesc = '1%'; } - return kindDesc; -} + return kindDesc; +}; export const parsePoolMetadata = (poolCard, aid1, aid2, assetList: IAsset[]) => { - let data = poolCard - if (aid1 === 0) { - data = {...data, metadata1: ASSET_BEAM} - } - else { - assetList.filter((item) => { - if (aid1 === item.aid) { - data = {...data, metadata1: item.parsedMetadata} - } - - }) - } - if (aid2 === 0) { - data = {...data, metadata2: ASSET_BEAM} - } else assetList.filter((item) => { - if (aid2 === item.aid) { - data = {...data, metadata2: item.parsedMetadata} - } - }) -return data -} + let data = poolCard; + if (aid1 === 0) { + data = { ...data, metadata1: ASSET_BEAM }; + } else { + assetList.filter((item) => { + if (aid1 === item.aid) { + data = { ...data, metadata1: item.parsedMetadata }; + } + return data; + }); + } + if (aid2 === 0) { + data = { ...data, metadata2: ASSET_BEAM }; + } else { + assetList.filter((item) => { + if (aid2 === item.aid) { + data = { ...data, metadata2: item.parsedMetadata }; + } + return data; + }); + } + return data; +}; export function fromGroths(value: number): number { - return value && value !== 0 ? value / GROTHS_IN_BEAM : 0; + return value && value !== 0 ? value / GROTHS_IN_BEAM : 0; } export function toGroths(value: number): number { - const val = Number(parseFloat((value * GROTHS_IN_BEAM).toString()).toPrecision(12)); - return value > 0 ? Math.floor(val) : 0; + const val = Number(parseFloat((value * GROTHS_IN_BEAM).toString()).toPrecision(12)); + return value > 0 ? Math.floor(val) : 0; } export function parseIntToNum(value:string):number { - const val = parseInt(value) - return parseInt(value) > 0 ? Math.floor(val) : 0; + const val = parseInt(value); + return parseInt(value) > 0 ? Math.floor(val) : 0; } export function checkTxStatus(txId:string, txList:ITxStatus[]) { - let status - txList.txs?.filter((item) => { - if (item.txId === txId) { - status = item.status - } - }) - return status + let status; + // TODO: type rr + // @ts-ignore + txList.txs.find((item) => { + if (item.txId === txId) { + status = item.status; + } + return status; + }); + return status; } export function setDataRequest(data) { - return {...data, bPredictOnly: 0} + return { ...data, bPredictOnly: 0 }; } -export function onFilter(data: IPoolCard[], filter, assetsList:IAsset[]){ - switch (filter) { - case 'all': { - return data - } - case 'my': { - return data.filter(el=>el.creator ) - } - case 'liquid': { - return data.filter(el=>el.ctl) - } - case 'empty': { - return data.filter(el=>!el.ctl) - } - default: - return data.filter(el=>el.ctl) +export function onFilter(data: IPoolCard[], filter) { + switch (filter) { + case 'all': { + return data; + } + case 'my': { + return data.filter((el) => el.creator); + } + case 'liquid': { + return data.filter((el) => el.ctl); } + case 'empty': { + return data.filter((el) => !el.ctl); + } + default: + return data.filter((el) => el.ctl); + } } -export const emptyPredict = (data: IPredict, amount: string | number ):boolean => { - return !data || !amount || amount === '0' -} +export const emptyPredict = (data: IPredict, amount: string | number):boolean => !data || !amount || amount === '0'; export function numFormatter(num) { - if (num > 999 && num < 1000000) { - return parseFloat((num / 1000).toFixed(2)) + 'K'; - } else if (num >= 1000000) { - return parseFloat((num / 1000000).toFixed(2)) + 'M'; - } else if (num <= 999){ - return parseFloat(num.toFixed(2)); - } + if (num > 999 && num < 1000000) { + return `${parseFloat((num / 1000).toFixed(2))}K`; + } if (num >= 1000000) { + return `${parseFloat((num / 1000000).toFixed(2))}M`; + } if (num <= 999) { + return parseFloat(num.toFixed(2)); + } + return num; } export const onPredictValue = (value, swap:boolean, predict: IPredict) => { - if(!predict || value.value == 0){ - return 0 - } - if(swap){ - return fromGroths(predict.tok1) - } - return fromGroths(predict.tok2) -} + if (!predict || value.value === 0) { + return 0; + } + if (swap) { + return fromGroths(predict.tok1); + } + return fromGroths(predict.tok2); +}; export const getFilterPools = (filtered: IOptions[], data:IPoolCard[]):IPoolCard[] => { - if (filtered.length !== 0) { - let newList = [] - filtered.map(el => { - data.map(item => { - if (item.aid1 == el.value || item.aid2 === el.value) { - return newList = [...newList, item] - } - }) - console.log(newList) - return newList - }) - return newList - }else return data - } - - - + if (filtered.length !== 0) { + let newList = []; + filtered.map((el) => { + data.map((item) => { + if (item.aid1 === el.value || item.aid2 === el.value) { + newList = [...newList, item]; + } + return newList; + }); + console.log(newList); + return newList; + }); + return newList; + } return data; +}; diff --git a/src/app/core/types.ts b/src/app/core/types.ts index a546ad8..f724dc3 100644 --- a/src/app/core/types.ts +++ b/src/app/core/types.ts @@ -3,139 +3,138 @@ export type Pallete = 'green' | 'ghost' | 'purple' | 'blue' | 'red' | 'white' | export type ButtonVariant = 'regular' | 'ghost' | 'ghostBordered' | 'block' | 'link' | 'icon' | 'control'; export enum Kind { - Low = 0, - Mid = 1, - High = 2, + Low = 0, + Mid = 1, + High = 2, } export enum KindProcent { - Low = '0.05%', - Mid = '0.03%', - High = '1%', + Low = '0.05%', + Mid = '0.03%', + High = '1%', } export enum TxStatus { - Pending = 0 , - InProgress = 1, - Canceled = 2, - Completed = 3, - Failed = 4, - Registering= 5 + Pending = 0, + InProgress = 1, + Canceled = 2, + Completed = 3, + Failed = 4, + Registering = 5, } export interface IMetadataPairs { - N: string; - NTHUN?: string; - SCH_VER?: string; - SN: string; - UN?: string; - OPT_COLOR?: string; + N: string; + NTHUN?: string; + SCH_VER?: string; + SN: string; + UN?: string; + OPT_COLOR?: string; } export interface IAsset { - aid: number, - metadata: string, - mintedHi: number, - mintedLo: number, - owner_pk: string, - parsedMetadata: IMetadataPairs, - limitHi?: number, - limitLo?: number, + aid: number, + metadata: string, + mintedHi: number, + mintedLo: number, + owner_pk: string, + parsedMetadata: IMetadataPairs, + limitHi?: number, + limitLo?: number, } export interface IPoolCard { - aid1: number, - aid2: number, - ctl: number, - k1_2: string, - k1_ctl: string, - k2_1: string, - k2_ctl: string, - kind: number, - lp_token: number, - tok1: number, - tok2: number, - metadata1?: IMetadataPairs, - metadata2?: IMetadataPairs, - creator?: number; + aid1: number, + aid2: number, + ctl: number, + k1_2: string, + k1_ctl: string, + k2_1: string, + k2_ctl: string, + kind: number, + lp_token: number, + tok1: number, + tok2: number, + metadata1?: IMetadataPairs, + metadata2?: IMetadataPairs, + creator?: number; } export interface ICreatePool{ - aid1: number, - aid2: number, - kind:number, + aid1: number, + aid2: number, + kind:number, } export interface IAddLiquidity extends ICreatePool{ - val1:number, - val2:number, - bPredictOnly: number + val1:number, + val2:number, + bPredictOnly: number } -export interface ITrade extends ICreatePool{ - val1_buy: number, - bPredictOnly?: number +export interface ITrade extends ICreatePool{ + val1_buy: number, + bPredictOnly?: number } export interface IWithdraw extends ICreatePool{ - ctl: number, - bPredictOnly?: number + ctl: number, + bPredictOnly?: number } - export interface ITsx { - txId: string; - asset_id: number; - comment: string; - fee: number; - kernel: string; - receiver: string; - sender: string; - status: number; - status_string: string; - tx_type: number; - tx_type_string: string; - failure_reason: string; - value: number; - create_time: number; - income: boolean; - rates: any[]; - sender_identity: string; - receiver_identity: string; - token: string; + txId: string; + asset_id: number; + comment: string; + fee: number; + kernel: string; + receiver: string; + sender: string; + status: number; + status_string: string; + tx_type: number; + tx_type_string: string; + failure_reason: string; + value: number; + create_time: number; + income: boolean; + rates: any[]; + sender_identity: string; + receiver_identity: string; + token: string; } export interface ITxStatus { - change: number; - change_str: string; - txs: ITsx[]; + change: number; + change_str: string; + txs: ITsx[]; } export interface ITxId { - txid: string + txid: string } export interface IPredict { - buy?: number, - fee_dao?: number, - fee_pool?: number, - pay?: number, - pay_raw?: number, - ctl?: number, - tok1?: number, - tok2?: number, + buy?: number, + fee_dao?: number, + fee_pool?: number, + pay?: number, + pay_raw?: number, + ctl?: number, + tok1?: number, + tok2?: number, } export interface ITxResult { - txid?: string, - res?: IPredict + txid?: string, + res?: IPredict } export interface IError { - answer: { - id: string, - jsonrpc: string - result: { - output: any - txid: string - } + answer: { + id: string, + jsonrpc: string + result: { + output: any + txid: string } - error: string; + } + error: string; } export interface IOptions { - value: number, - label: string + value: number, + label: string, } diff --git a/src/app/core/utils.js b/src/app/core/utils.js index f6366aa..2fb8710 100644 --- a/src/app/core/utils.js +++ b/src/app/core/utils.js @@ -1,749 +1,739 @@ const MIN_AMOUNT = 0.00000001; const MAX_AMOUNT = 254000000; -let BEAM = null -let CallID = 0 -let Calls = {} -let APIResCB = undefined -let headlessNode = "eu-node01.masternet.beam.mw:8200" -let InitParams = undefined +let BEAM = null; +let CallID = 0; +const Calls = {}; +let APIResCB; +const headlessNode = 'eu-node01.masternet.beam.mw:8200'; +let InitParams; export default class Utils { - static isMobile () { - const ua = navigator.userAgent; - return (/android/i.test(ua) || /iPad|iPhone|iPod/.test(ua)); - } - - static isAndroid () { - const ua = navigator.userAgent; - return (/android/i.test(ua)); - } - - static isDesktop () { - const ua = navigator.userAgent; - return (/QtWebEngine/i.test(ua)); - } - - static isWeb () { - return !Utils.isDesktop() && !Utils.isMobile() - } - - static isHeadless () { - return BEAM && BEAM.headless - } - - static isChrome () { - const userAgent = navigator.userAgent; - const isChrome = userAgent.match(/chrome|chromium|crios/i) - return isChrome && userAgent.indexOf("Edg") == -1 - } - - static async createMobileAPI(apirescback) { - return new Promise((resolve) => { - if (Utils.isAndroid()) { - document.addEventListener("onCallWalletApiResult", (res) => { - apirescback(res.detail) - }) - } - else { - window.BEAM.callWalletApiResult(apirescback); - } - resolve({ - api: window.BEAM - }); - }) - } - - static async createDesktopAPI(apirescback) { - await Utils.injectScript("qrc:///qtwebchannel/qwebchannel.js") - return new Promise(async (resolve) => { - new QWebChannel(qt.webChannelTransport, (channel) => { - channel.objects.BEAM.api.callWalletApiResult.connect(apirescback) - resolve({ - api: channel.objects.BEAM.api, - styles: channel.objects.BEAM.style - }) - }) - }) - } - - static async createWebAPI(apiver, apivermin, appname, apirescback) { - return new Promise((resolve) => { - window.addEventListener('message', async (ev) => { - if (ev.data === 'apiInjected') { - await window.BeamApi.callWalletApiResult(apirescback); - resolve({ - api: window.BeamApi - }) - } - }, false); - window.postMessage({type: "create_beam_api", apiver, apivermin, appname}, window.origin); - }) - } - - static async createHeadlessAPI(apiver, apivermin, appname, apirescback) { - await Utils.injectScript("wasm-client.js") - - let WasmModule = await BeamModule() - let WasmWalletClient = WasmModule.WasmWalletClient - let client = new WasmWalletClient(headlessNode) - client.startWallet() - - client.subscribe((response) => { - let err = "Unexpected wasm wallet client response call: " + response - console.log(err) - throw err - }) - - client.setApproveContractInfoHandler((info) => { - let err = "Unexpected wasm wallet client transaction in headless wallet: " + info - console.log(err) - throw err - }) - - return new Promise((resolve, reject) => { - let appid = WasmWalletClient.GenerateAppID(appname, window.location.href) - client.createAppAPI(apiver, apivermin, appid, appname, (err, api) => { - if (err) { - reject(err) - } - - api.setHandler(apirescback) - resolve({ - headless: true, - module: WasmModule, - factory: WasmWalletClient, - client, - appid, - api - }) - }) - }) - } - - static async stopHeadlessWallet() { - return new Promise((resolve, reject) => { - BEAM.client.stopWallet((data) => { - const running = BEAM.client.isRunning(); - console.log(`is running: ${BEAM.client.isRunning()}`); - console.log('wallet stopped:', data); - - if (running) { - reject(false); - } else { - resolve(true); - } - }) - }) - } - - static async switchToWebAPI () { - if (!Utils.isHeadless()) { - throw "Wallet must be opened in a headless mode" - } - - let apiver = InitParams["api_version"] || "current" - let apivermin = InitParams["min_api_version"] || "" - let appname = InitParams["appname"] - let apirescb = (...args) => Utils.handleApiResult(...args) - - const newAPI = await new Promise((resolve) => { - const listener = async (ev) => { - if (ev.data === 'apiInjected') { - await window.BeamApi.callWalletApiResult(apirescb) - Utils.hideLoading() - resolve(window.BeamApi) - } - - if (ev.data === 'rejected') { - } - } - - window.addEventListener('message', listener, false) - Utils.showLoading({ - headless: true, - connecting: true, - onCancel: (res) => { - Utils.hideLoading() - window.removeEventListener('message', listener) - //TODO: add cancel handling in wallet - window.postMessage({type: "cancel_beam_api", apiver, apivermin, appname}, window.origin); - resolve(res) - }, - onReconnect: () => { - window.postMessage({type: "retry_beam_api", apiver, apivermin, appname}, window.origin); - } - }) - window.postMessage({type: "create_beam_api", apiver, apivermin, appname}, window.origin) - }) - - if (newAPI) { - BEAM.api.delete(); - await Utils.stopHeadlessWallet(); - BEAM = { - api: newAPI - }; - } - - return newAPI - } - - static callApi(method, params, cback) { - let callid = ['call', CallID++].join('-') - Calls[callid] = cback - - let request = { - "jsonrpc": "2.0", - "id": callid, - "method": method, - "params": params - } - - console.log(Utils.formatJSON(request)) - if (Utils.isHeadless()) { - return BEAM.api.callWalletApi(JSON.stringify(request)) - } - - if (Utils.isWeb()) { - return BEAM.api.callWalletApi(callid, method, params); - } - - if (Utils.isMobile()) { - return BEAM.api.callWalletApi(JSON.stringify(request)); + static isMobile() { + const ua = navigator.userAgent; + return (/android/i.test(ua) || /iPad|iPhone|iPod/.test(ua)); + } + + static isAndroid() { + const ua = navigator.userAgent; + return (/android/i.test(ua)); + } + + static isDesktop() { + const ua = navigator.userAgent; + return (/QtWebEngine/i.test(ua)); + } + + static isWeb() { + return !Utils.isDesktop() && !Utils.isMobile(); + } + + static isHeadless() { + return BEAM && BEAM.headless; + } + + static isChrome() { + const { userAgent } = navigator; + const isChrome = userAgent.match(/chrome|chromium|crios/i); + return isChrome && userAgent.indexOf('Edg') == -1; + } + + static async createMobileAPI(apirescback) { + return new Promise((resolve) => { + if (Utils.isAndroid()) { + document.addEventListener('onCallWalletApiResult', (res) => { + apirescback(res.detail); + }); + } else { + window.BEAM.callWalletApiResult(apirescback); + } + resolve({ + api: window.BEAM, + }); + }); + } + + static async createDesktopAPI(apirescback) { + await Utils.injectScript('qrc:///qtwebchannel/qwebchannel.js'); + return new Promise(async (resolve) => { + new QWebChannel(qt.webChannelTransport, (channel) => { + channel.objects.BEAM.api.callWalletApiResult.connect(apirescback); + resolve({ + api: channel.objects.BEAM.api, + styles: channel.objects.BEAM.style, + }); + }); + }); + } + + static async createWebAPI(apiver, apivermin, appname, apirescback) { + return new Promise((resolve) => { + window.addEventListener('message', async (ev) => { + if (ev.data === 'apiInjected') { + await window.BeamApi.callWalletApiResult(apirescback); + resolve({ + api: window.BeamApi, + }); } - - if (Utils.isDesktop()) { - return BEAM.api.callWalletApi(JSON.stringify(request)); + }, false); + window.postMessage({ + type: 'create_beam_api', apiver, apivermin, appname, + }, window.origin); + }); + } + + static async createHeadlessAPI(apiver, apivermin, appname, apirescback) { + await Utils.injectScript('wasm-client.js'); + + const WasmModule = await BeamModule(); + const { WasmWalletClient } = WasmModule; + const client = new WasmWalletClient(headlessNode); + client.startWallet(); + + client.subscribe((response) => { + const err = `Unexpected wasm wallet client response call: ${response}`; + console.log(err); + throw err; + }); + + client.setApproveContractInfoHandler((info) => { + const err = `Unexpected wasm wallet client transaction in headless wallet: ${info}`; + console.log(err); + throw err; + }); + + return new Promise((resolve, reject) => { + const appid = WasmWalletClient.GenerateAppID(appname, window.location.href); + client.createAppAPI(apiver, apivermin, appid, appname, (err, api) => { + if (err) { + reject(err); } - } - static invokeContract(args, cback, bytes) { - let params = { - "create_tx": false + api.setHandler(apirescback); + resolve({ + headless: true, + module: WasmModule, + factory: WasmWalletClient, + client, + appid, + api, + }); + }); + }); + } + + static async stopHeadlessWallet() { + return new Promise((resolve, reject) => { + BEAM.client.stopWallet((data) => { + const running = BEAM.client.isRunning(); + console.log(`is running: ${BEAM.client.isRunning()}`); + console.log('wallet stopped:', data); + + if (running) { + reject(false); + } else { + resolve(true); } - - if (args) { - params = Object.assign({ - "args": args - }, params) + }); + }); + } + + static async switchToWebAPI() { + if (!Utils.isHeadless()) { + throw 'Wallet must be opened in a headless mode'; + } + + const apiver = InitParams.api_version || 'current'; + const apivermin = InitParams.min_api_version || ''; + const { appname } = InitParams; + const apirescb = (...args) => Utils.handleApiResult(...args); + + const newAPI = await new Promise((resolve) => { + const listener = async (ev) => { + if (ev.data === 'apiInjected') { + await window.BeamApi.callWalletApiResult(apirescb); + Utils.hideLoading(); + resolve(window.BeamApi); } - if (bytes) { - params = Object.assign({ - "contract": bytes - }, params) + if (ev.data === 'rejected') { } + }; + + window.addEventListener('message', listener, false); + Utils.showLoading({ + headless: true, + connecting: true, + onCancel: (res) => { + Utils.hideLoading(); + window.removeEventListener('message', listener); + // TODO: add cancel handling in wallet + window.postMessage({ + type: 'cancel_beam_api', apiver, apivermin, appname, + }, window.origin); + resolve(res); + }, + onReconnect: () => { + window.postMessage({ + type: 'retry_beam_api', apiver, apivermin, appname, + }, window.origin); + }, + }); + window.postMessage({ + type: 'create_beam_api', apiver, apivermin, appname, + }, window.origin); + }); + + if (newAPI) { + BEAM.api.delete(); + await Utils.stopHeadlessWallet(); + BEAM = { + api: newAPI, + }; + } + + return newAPI; + } + + static callApi(method, params, cback) { + const callid = ['call', CallID++].join('-'); + Calls[callid] = cback; + + const request = { + jsonrpc: '2.0', + id: callid, + method, + params, + }; - return Utils.callApi('invoke_contract', params, cback) + console.log(Utils.formatJSON(request)); + if (Utils.isHeadless()) { + return BEAM.api.callWalletApi(JSON.stringify(request)); } - static handleApiResult (json) { - let answer = undefined - try - { - answer = JSON.parse(json); - - if (answer.result && answer.result.output) { - //console.log('Output: ', JSON.parse(answer.result.output)); - } else { - //console.log('Api result: ', answer); - } - - const id = answer.id - const cback = Calls[id] || APIResCB - delete Calls[id] - - if (answer.error) { - return cback(answer) - } - - if (typeof answer.result == 'undefined') { - return cback({ - error: "no valid api call result", - answer - }) - } - - if (typeof answer.result.output == 'string') { - // this is shader result - let shaderAnswer = JSON.parse(answer.result.output) - if (shaderAnswer.error) { - return cback({ - error: shaderAnswer.error, - answer - }) - } - return cback(null, shaderAnswer, answer) - } - else - { - return cback(null, answer.result, answer) - } - } - catch (err) - { - APIResCB({ - error: err.toString(), - answer: answer || json - }) - } + if (Utils.isWeb()) { + return BEAM.api.callWalletApi(callid, method, params); } - static async initialize(params, initcback) { - InitParams = params - APIResCB = params["apiResultHandler"] - let headless = params["headless"] - - try - { - if (Utils.isDesktop()) { - BEAM = await Utils.createDesktopAPI((...args) => Utils.handleApiResult(...args)) - } - - if (Utils.isWeb()) { - let apiver = params["api_version"] || "current" - let apivermin = params["min_api_version"] || "" - let appname = params["appname"] - - if (!Utils.isChrome()) { - Utils.showChromeDownload(); - return false; - } - - if (headless) { - Utils.showLoading({ - headless: true, - connecting: false - }); - BEAM = await Utils.createHeadlessAPI( - apiver, apivermin, appname, - (...args) => Utils.handleApiResult(...args) - ) - } else { - Utils.showLoading({ - headless: false, - connecting: true - }); - BEAM = await Utils.createWebAPI( - apiver, apivermin, appname, - (...args) => Utils.handleApiResult(...args) - ) - } - } - - if (Utils.isMobile()) { - try { - BEAM = await Utils.createMobileAPI((...args) => Utils.handleApiResult(...args)); - } catch (e) { - Utils.showMobileStoresLinks(); - return false; - } - } - - let styles = Utils.getStyles() - Utils.applyStyles(styles) - Utils.hideLoading() - - if (!BEAM) { - return initcback("Failed to create BEAM API") - } - - return initcback(null) - } - catch (err) - { - return initcback(err) - } + if (Utils.isMobile()) { + return BEAM.api.callWalletApi(JSON.stringify(request)); } - static getStyles () { - if (BEAM && BEAM.styles) { - // TODO: проборосить стили из мобайла и экстеншена - return BEAM.styles - } - - return { - appsGradientOffset: -174, - appsGradientTop: 56, - content_main: "#ffffff", - background_main_top: "#035b8f", - background_main: "#042548", - background_popup: "#00446c", - validator_error: "#ff625c" - } + if (Utils.isDesktop()) { + return BEAM.api.callWalletApi(JSON.stringify(request)); } + } - static applyStyles(style) { - if (!Utils.isDesktop()) { - document.head.innerHTML += ''; - } - - if (Utils.isMobile()) { - document.body.classList.add('mobile'); - } + static invokeContract(args, cback, bytes) { + let params = { + create_tx: false, + }; - if (Utils.isWeb()) { - document.body.classList.add('web'); - } + if (args) { + params = { args, ...params }; } - // - // Convenience functions - // - static reload () { - window.location.reload(); + if (bytes) { + params = { contract: bytes, ...params }; } - static async injectScript(url) { - return new Promise((resolve, reject) => { - let js = document.createElement('script'); - js.type = 'text/javascript'; - js.async = true; - js.src = url; - js.onload = () => resolve() - js.onerror = (err) => reject(err) - document.getElementsByTagName('head')[0].appendChild(js); - }) - } + return Utils.callApi('invoke_contract', params, cback); + } - static hex2rgba = (hex, alpha = 1) => { - const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16)); - return `rgba(${r},${g},${b},${alpha})`; - }; + static handleApiResult(json) { + let answer; + try { + answer = JSON.parse(json); - static getById = (id) => { - return document.getElementById(id); - } + if (answer.result && answer.result.output) { + // console.log('Output: ', JSON.parse(answer.result.output)); + } else { + // console.log('Api result: ', answer); + } - static setText(id, text) { - Utils.getById(id).innerText = text; - } - - static show(id) { - Utils.getById(id).classList.remove("hidden"); - } + const { id } = answer; + const cback = Calls[id] || APIResCB; + delete Calls[id]; - static hide(id) { - Utils.getById(id).classList.add("hidden"); - } + if (answer.error) { + return cback(answer); + } - static download(url, cback) { - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if(xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - let buffer = xhr.response; - let byteArray = new Uint8Array(buffer); - let array = Array.from(byteArray); - - if (!array || !array.length) { - return cback("empty shader"); - } - - return cback(null, array); - } else { - let errMsg = ["code", xhr.status].join(" "); - return cback(errMsg); - } - } + if (typeof answer.result === 'undefined') { + return cback({ + error: 'no valid api call result', + answer, + }); + } + + if (typeof answer.result.output === 'string') { + // this is shader result + const shaderAnswer = JSON.parse(answer.result.output); + if (shaderAnswer.error) { + return cback({ + error: shaderAnswer.error, + answer, + }); } - xhr.open('GET', url, true); - xhr.responseType = "arraybuffer"; - xhr.send(null); - } - - static handleString(next) { - let result = true; - const regex = new RegExp(/^-?\d+(\.\d*)?$/g); - const floatValue = parseFloat(next); - const afterDot = next.indexOf('.') > 0 ? next.substring(next.indexOf('.') + 1) : '0'; - if ((next && !String(next).match(regex)) || - (String(next).length > 1 && String(next)[0] === '0' && next.indexOf('.') < 0) || - (parseInt(afterDot, 10) === 0 && afterDot.length > 7) || - (afterDot.length > 8) || - (floatValue === 0 && next.length > 1 && next[1] !== '.') || - (floatValue < 1 && next.length > 10) || - (floatValue > 0 && (floatValue < MIN_AMOUNT || floatValue > MAX_AMOUNT))) { - result = false; + return cback(null, shaderAnswer, answer); + } + + return cback(null, answer.result, answer); + } catch (err) { + APIResCB({ + error: err.toString(), + answer: answer || json, + }); + } + } + + static async initialize(params, initcback) { + InitParams = params; + APIResCB = params.apiResultHandler; + const { headless } = params; + + try { + if (Utils.isDesktop()) { + BEAM = await Utils.createDesktopAPI((...args) => Utils.handleApiResult(...args)); + } + + if (Utils.isWeb()) { + const apiver = params.api_version || 'current'; + const apivermin = params.min_api_version || ''; + const { appname } = params; + + if (!Utils.isChrome()) { + Utils.showChromeDownload(); + return false; } - return result; - } - static showLoading(params) { - const {headless, connecting, onCancel, onReconnect} = params; - - const styles = Utils.getStyles() - Utils.applyStyles(styles); - const topColor = [styles.appsGradientOffset, "px,"].join(''); - const mainColor = [styles.appsGradientTop, "px,"].join(''); - - let bg = document.createElement("div"); - bg.style.width = "100%"; - bg.style.height = "100%"; - bg.style.color = "#fff"; - bg.id = "dapp-loader"; - bg.style.position = "absolute"; - if (headless && connecting) { - bg.style.top = '0'; - bg.style.left = '0'; - bg.style.position = 'fixed'; - bg.addEventListener('click', (ev) => { - ev.stopPropagation() - if (ev.target.id === "dapp-loader") { - onCancel() - } - }); + if (headless) { + Utils.showLoading({ + headless: true, + connecting: false, + }); + BEAM = await Utils.createHeadlessAPI( + apiver, apivermin, appname, + (...args) => Utils.handleApiResult(...args), + ); } else { - bg.style.backgroundImage = [ - "linear-gradient(to bottom,", - styles.background_main_top, topColor, - styles.background_main, mainColor, - styles.background_main - ].join(' '); + Utils.showLoading({ + headless: false, + connecting: true, + }); + BEAM = await Utils.createWebAPI( + apiver, apivermin, appname, + (...args) => Utils.handleApiResult(...args), + ); } - let loadContainer = document.createElement("div"); - loadContainer.id = "dapp-loading"; - - loadContainer.style.textAlign = 'center'; - loadContainer.style.margin = '50px auto 0 auto'; - loadContainer.style.width = '585px'; - loadContainer.style.padding = '5%'; - loadContainer.style.borderRadius = '10px'; - - let titleElem = null; - let subtitle = null; - - if (connecting) { - titleElem = document.createElement("h3"); - titleElem.innerText = "Connecting to BEAM Web Wallet."; - subtitle = document.createElement("p"); - subtitle.innerText = ["To use ", InitParams["appname"], " you should have BEAM Web Wallet installed and allow connection."].join("") - - if (headless) { - loadContainer.style.backgroundColor = 'rgba(3, 91, 133, 0.95)'; - const container = document.getElementById('container'); - if (container) { - container.style.filter = 'blur(3px)' - } - } else { - loadContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.05)'; - } - } else { - loadContainer.style.backgroundColor = 'transparent'; - - titleElem = document.createElement("div"); - titleElem.style.fontSize = '25px'; - titleElem.style.fontWeight = '400'; - titleElem.innerText = [InitParams["appname"], "is loading"].join(' '); - subtitle = document.createElement("p"); - subtitle.innerText = "Please wait..."; + } + + if (Utils.isMobile()) { + try { + BEAM = await Utils.createMobileAPI((...args) => Utils.handleApiResult(...args)); + } catch (e) { + Utils.showMobileStoresLinks(); + return false; } + } - loadContainer.appendChild(titleElem); - loadContainer.appendChild(subtitle); - - if (connecting) { - let reconnectButton = document.createElement("button"); - reconnectButton.innerText = "Try to connect again"; - reconnectButton.style.height = "44px"; - reconnectButton.style.padding = "13px 30px"; - reconnectButton.style.borderRadius = "50px"; - reconnectButton.style.border = "none"; - reconnectButton.style.color = "#fff"; - reconnectButton.style.cursor = "pointer"; - reconnectButton.style.fontWeight = "bold"; - reconnectButton.style.fontSize = "14px"; - reconnectButton.style.backgroundColor = "rgba(255, 255, 255, 0.1)"; - - reconnectButton.addEventListener("mouseover", () => { - reconnectButton.style.boxShadow = "0 0 8px white"; - }, false); - - reconnectButton.addEventListener("mouseout", () => { - reconnectButton.style.boxShadow = "none"; - }, false); - - reconnectButton.addEventListener('click', onReconnect); - - let installButton = document.createElement("button"); - installButton.innerText = "Install BEAM Web Wallet"; - installButton.style.height = "44px"; - installButton.style.padding = "13px 30px"; - installButton.style.borderRadius = "50px"; - installButton.style.border = "none"; - installButton.style.color = "#042548"; - installButton.style.cursor = "pointer"; - installButton.style.fontWeight = "bold"; - installButton.style.fontSize = "14px"; - installButton.style.backgroundColor = "#00f6d2"; - installButton.addEventListener('click', () => { - window.open('https://chrome.google.com/webstore/detail/beam-web-wallet/ilhaljfiglknggcoegeknjghdgampffk', - '_blank'); - }); - - installButton.addEventListener("mouseover", () => { - installButton.style.boxShadow = "0 0 8px white"; - }, false); - installButton.addEventListener("mouseout", () => { - installButton.style.boxShadow = "none"; - }, false); - installButton.style.marginLeft = '30px'; - - let controlsArea = document.createElement("div"); - controlsArea.style.marginTop = "50px"; - - loadContainer.appendChild(controlsArea); - controlsArea.appendChild(reconnectButton); - controlsArea.appendChild(installButton); - } + const styles = Utils.getStyles(); + Utils.applyStyles(styles); + Utils.hideLoading(); - bg.appendChild(loadContainer); + if (!BEAM) { + return initcback('Failed to create BEAM API'); + } - document.body.appendChild(bg); + return initcback(null); + } catch (err) { + return initcback(err); } + } - static showChromeDownload() { - const styles = Utils.getStyles() - Utils.applyStyles(styles); - const topColor = [styles.appsGradientOffset, "px,"].join(''); - const mainColor = [styles.appsGradientTop, "px,"].join(''); - - let bg = document.createElement("div"); - bg.style.width = "100%"; - bg.style.height = "100%"; - bg.style.color = "#fff"; - bg.id = "chrome-download"; - bg.style.position = "absolute"; - bg.style.textAlign = "center"; - bg.style.backgroundImage = [ - "linear-gradient(to bottom,", - styles.background_main_top, topColor, - styles.background_main, mainColor, - styles.background_main - ].join(' '); - - let notSupp = document.createElement("p"); - notSupp.innerText = "Your browser is not supported"; - notSupp.style.color = "#fff"; - notSupp.style.fontWeight = "bold"; - notSupp.style.fontSize = "18px"; - notSupp.style.marginTop = "200px"; - let download = document.createElement("p"); - download.innerText = "Download chrome browser"; - download.style.cursor = "pointer"; - download.style.color = "#00f6d2"; - - download.addEventListener('click', () => { - window.open('https://www.google.com/chrome/', - '_blank'); - }); - - bg.appendChild(notSupp); - bg.appendChild(download); - - document.body.appendChild(bg); + static getStyles() { + if (BEAM && BEAM.styles) { + // TODO: проборосить стили из мобайла и экстеншена + return BEAM.styles; } - static showMobileStoresLinks() { - const styles = Utils.getStyles() - Utils.applyStyles(styles); - const topColor = [styles.appsGradientOffset, "px,"].join(''); - const mainColor = [styles.appsGradientTop, "px,"].join(''); - - let bg = document.createElement("div"); - bg.style.width = "100%"; - bg.style.height = "100%"; - bg.style.color = "#fff"; - bg.id = "chrome-download"; - bg.style.position = "absolute"; - bg.style.textAlign = "center"; - bg.style.backgroundImage = [ - "linear-gradient(to bottom,", - styles.background_main_top, topColor, - styles.background_main, mainColor, - styles.background_main - ].join(' '); - - let downloadLink = document.createElement("p"); - downloadLink.innerText = "Download beam wallet"; - downloadLink.style.marginTop = "100px"; - downloadLink.style.fontSize = "20px"; - downloadLink.style.color = "#00f6d2"; - downloadLink.addEventListener('click', () => { - Utils.isAndroid() - ? window.open('https://play.google.com/store/apps/details?id=com.mw.beam.beamwallet.mainnet', - '_blank') - : window.open('https://apps.apple.com/us/app/beam-privacy-wallet/id1459842353?ls=1', - '_blank'); - }); - - bg.appendChild(downloadLink); - document.body.appendChild(bg); - } - - static hideLoading() { - const loader = document.getElementById("dapp-loader") - if (loader) { - loader.parentNode.removeChild(loader) - } - - const container = document.getElementById('container') - if (container) { - container.style.filter = 'none' - } - } - - static formateValue(value) { - if (value > 0) { - return parseFloat(value.toFixed(2)).toString(); - } else { - return value; - } - } - - static numberWithCommas(x) { - if (x > 0) { - return x.replace(/\B(?=(\d{3})+(?!\d))/g, ","); - } else { - return x; - } - } - - static getRateStr(value, rate) { - const rateVal = Utils.formateValue(new Big(value).times(rate)); - return (rate > 0 && value > 0 - ? (rateVal > 0.1 ? (Utils.numberWithCommas(rateVal) + ' USD') : '< 1 cent') - : '0 USD'); - } - - static ensureField(obj, name, type) { - if (obj[name] == undefined) { - throw `No '${name}' field on object` + return { + appsGradientOffset: -174, + appsGradientTop: 56, + content_main: '#ffffff', + background_main_top: '#035b8f', + background_main: '#042548', + background_popup: '#00446c', + validator_error: '#ff625c', + }; + } + + static applyStyles(style) { + if (!Utils.isDesktop()) { + document.head.innerHTML += ''; + } + + if (Utils.isMobile()) { + document.body.classList.add('mobile'); + } + + if (Utils.isWeb()) { + document.body.classList.add('web'); + } + } + + // + // Convenience functions + // + static reload() { + window.location.reload(); + } + + static async injectScript(url) { + return new Promise((resolve, reject) => { + const js = document.createElement('script'); + js.type = 'text/javascript'; + js.async = true; + js.src = url; + js.onload = () => resolve(); + js.onerror = (err) => reject(err); + document.getElementsByTagName('head')[0].appendChild(js); + }); + } + + static hex2rgba = (hex, alpha = 1) => { + const [r, g, b] = hex.match(/\w\w/g).map((x) => parseInt(x, 16)); + return `rgba(${r},${g},${b},${alpha})`; + }; + + static getById = (id) => document.getElementById(id); + + static setText(id, text) { + Utils.getById(id).innerText = text; + } + + static show(id) { + Utils.getById(id).classList.remove('hidden'); + } + + static hide(id) { + Utils.getById(id).classList.add('hidden'); + } + + static download(url, cback) { + const xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + const buffer = xhr.response; + const byteArray = new Uint8Array(buffer); + const array = Array.from(byteArray); + + if (!array || !array.length) { + return cback('empty shader'); + } + + return cback(null, array); } - - if (type == 'array') { - if (!Array.isArray(obj[name])) { - throw `${name} is expected to be an array` - } - return + const errMsg = ['code', xhr.status].join(' '); + return cback(errMsg); + } + }; + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + } + + static handleString(next) { + let result = true; + const regex = new RegExp(/^-?\d+(\.\d*)?$/g); + const floatValue = parseFloat(next); + const afterDot = next.indexOf('.') > 0 ? next.substring(next.indexOf('.') + 1) : '0'; + if ((next && !String(next).match(regex)) + || (String(next).length > 1 && String(next)[0] === '0' && next.indexOf('.') < 0) + || (parseInt(afterDot, 10) === 0 && afterDot.length > 7) + || (afterDot.length > 8) + || (floatValue === 0 && next.length > 1 && next[1] !== '.') + || (floatValue < 1 && next.length > 10) + || (floatValue > 0 && (floatValue < MIN_AMOUNT || floatValue > MAX_AMOUNT))) { + result = false; + } + return result; + } + + static showLoading(params) { + const { + headless, connecting, onCancel, onReconnect, + } = params; + + const styles = Utils.getStyles(); + Utils.applyStyles(styles); + const topColor = [styles.appsGradientOffset, 'px,'].join(''); + const mainColor = [styles.appsGradientTop, 'px,'].join(''); + + const bg = document.createElement('div'); + bg.style.width = '100%'; + bg.style.height = '100%'; + bg.style.color = '#fff'; + bg.id = 'dapp-loader'; + bg.style.position = 'absolute'; + if (headless && connecting) { + bg.style.top = '0'; + bg.style.left = '0'; + bg.style.position = 'fixed'; + bg.addEventListener('click', (ev) => { + ev.stopPropagation(); + if (ev.target.id === 'dapp-loader') { + onCancel(); } - - if (type) { - let tof = typeof obj[name] - if (tof !== type) { - throw `Bad type '${tof}' for '${name}'. '${type}' expected.` - } - return + }); + } else { + bg.style.backgroundImage = [ + 'linear-gradient(to bottom,', + styles.background_main_top, topColor, + styles.background_main, mainColor, + styles.background_main, + ].join(' '); + } + const loadContainer = document.createElement('div'); + loadContainer.id = 'dapp-loading'; + + loadContainer.style.textAlign = 'center'; + loadContainer.style.margin = '50px auto 0 auto'; + loadContainer.style.width = '585px'; + loadContainer.style.padding = '5%'; + loadContainer.style.borderRadius = '10px'; + + let titleElem = null; + let subtitle = null; + + if (connecting) { + titleElem = document.createElement('h3'); + titleElem.innerText = 'Connecting to BEAM Web Wallet.'; + subtitle = document.createElement('p'); + subtitle.innerText = ['To use ', InitParams.appname, ' you should have BEAM Web Wallet installed and allow connection.'].join(''); + + if (headless) { + loadContainer.style.backgroundColor = 'rgba(3, 91, 133, 0.95)'; + const container = document.getElementById('container'); + if (container) { + container.style.filter = 'blur(3px)'; } - } - - static isUserCancelled (err) { - return err.error && err.error.code == -32021 - } - - static formatJSON(obj) { - let res = JSON.stringify(obj, null, 2) - return res == "{}" ? obj.toString() : res - } + } else { + loadContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.05)'; + } + } else { + loadContainer.style.backgroundColor = 'transparent'; + + titleElem = document.createElement('div'); + titleElem.style.fontSize = '25px'; + titleElem.style.fontWeight = '400'; + titleElem.innerText = [InitParams.appname, 'is loading'].join(' '); + subtitle = document.createElement('p'); + subtitle.innerText = 'Please wait...'; + } + + loadContainer.appendChild(titleElem); + loadContainer.appendChild(subtitle); + + if (connecting) { + const reconnectButton = document.createElement('button'); + reconnectButton.innerText = 'Try to connect again'; + reconnectButton.style.height = '44px'; + reconnectButton.style.padding = '13px 30px'; + reconnectButton.style.borderRadius = '50px'; + reconnectButton.style.border = 'none'; + reconnectButton.style.color = '#fff'; + reconnectButton.style.cursor = 'pointer'; + reconnectButton.style.fontWeight = 'bold'; + reconnectButton.style.fontSize = '14px'; + reconnectButton.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'; + + reconnectButton.addEventListener('mouseover', () => { + reconnectButton.style.boxShadow = '0 0 8px white'; + }, false); + + reconnectButton.addEventListener('mouseout', () => { + reconnectButton.style.boxShadow = 'none'; + }, false); + + reconnectButton.addEventListener('click', onReconnect); + + const installButton = document.createElement('button'); + installButton.innerText = 'Install BEAM Web Wallet'; + installButton.style.height = '44px'; + installButton.style.padding = '13px 30px'; + installButton.style.borderRadius = '50px'; + installButton.style.border = 'none'; + installButton.style.color = '#042548'; + installButton.style.cursor = 'pointer'; + installButton.style.fontWeight = 'bold'; + installButton.style.fontSize = '14px'; + installButton.style.backgroundColor = '#00f6d2'; + installButton.addEventListener('click', () => { + window.open('https://chrome.google.com/webstore/detail/beam-web-wallet/ilhaljfiglknggcoegeknjghdgampffk', + '_blank'); + }); + + installButton.addEventListener('mouseover', () => { + installButton.style.boxShadow = '0 0 8px white'; + }, false); + installButton.addEventListener('mouseout', () => { + installButton.style.boxShadow = 'none'; + }, false); + installButton.style.marginLeft = '30px'; + + const controlsArea = document.createElement('div'); + controlsArea.style.marginTop = '50px'; + + loadContainer.appendChild(controlsArea); + controlsArea.appendChild(reconnectButton); + controlsArea.appendChild(installButton); + } + + bg.appendChild(loadContainer); + + document.body.appendChild(bg); + } + + static showChromeDownload() { + const styles = Utils.getStyles(); + Utils.applyStyles(styles); + const topColor = [styles.appsGradientOffset, 'px,'].join(''); + const mainColor = [styles.appsGradientTop, 'px,'].join(''); + + const bg = document.createElement('div'); + bg.style.width = '100%'; + bg.style.height = '100%'; + bg.style.color = '#fff'; + bg.id = 'chrome-download'; + bg.style.position = 'absolute'; + bg.style.textAlign = 'center'; + bg.style.backgroundImage = [ + 'linear-gradient(to bottom,', + styles.background_main_top, topColor, + styles.background_main, mainColor, + styles.background_main, + ].join(' '); + + const notSupp = document.createElement('p'); + notSupp.innerText = 'Your browser is not supported'; + notSupp.style.color = '#fff'; + notSupp.style.fontWeight = 'bold'; + notSupp.style.fontSize = '18px'; + notSupp.style.marginTop = '200px'; + const download = document.createElement('p'); + download.innerText = 'Download chrome browser'; + download.style.cursor = 'pointer'; + download.style.color = '#00f6d2'; + + download.addEventListener('click', () => { + window.open('https://www.google.com/chrome/', + '_blank'); + }); + + bg.appendChild(notSupp); + bg.appendChild(download); + + document.body.appendChild(bg); + } + + static showMobileStoresLinks() { + const styles = Utils.getStyles(); + Utils.applyStyles(styles); + const topColor = [styles.appsGradientOffset, 'px,'].join(''); + const mainColor = [styles.appsGradientTop, 'px,'].join(''); + + const bg = document.createElement('div'); + bg.style.width = '100%'; + bg.style.height = '100%'; + bg.style.color = '#fff'; + bg.id = 'chrome-download'; + bg.style.position = 'absolute'; + bg.style.textAlign = 'center'; + bg.style.backgroundImage = [ + 'linear-gradient(to bottom,', + styles.background_main_top, topColor, + styles.background_main, mainColor, + styles.background_main, + ].join(' '); + + const downloadLink = document.createElement('p'); + downloadLink.innerText = 'Download beam wallet'; + downloadLink.style.marginTop = '100px'; + downloadLink.style.fontSize = '20px'; + downloadLink.style.color = '#00f6d2'; + downloadLink.addEventListener('click', () => { + Utils.isAndroid() + ? window.open('https://play.google.com/store/apps/details?id=com.mw.beam.beamwallet.mainnet', + '_blank') + : window.open('https://apps.apple.com/us/app/beam-privacy-wallet/id1459842353?ls=1', + '_blank'); + }); + + bg.appendChild(downloadLink); + document.body.appendChild(bg); + } + + static hideLoading() { + const loader = document.getElementById('dapp-loader'); + if (loader) { + loader.parentNode.removeChild(loader); + } + + const container = document.getElementById('container'); + if (container) { + container.style.filter = 'none'; + } + } + + static formateValue(value) { + if (value > 0) { + return parseFloat(value.toFixed(2)).toString(); + } + return value; + } + + static numberWithCommas(x) { + if (x > 0) { + return x.replace(/\B(?=(\d{3})+(?!\d))/g, ','); + } + return x; + } + + static getRateStr(value, rate) { + const rateVal = Utils.formateValue(new Big(value).times(rate)); + return (rate > 0 && value > 0 + ? (rateVal > 0.1 ? (`${Utils.numberWithCommas(rateVal)} USD`) : '< 1 cent') + : '0 USD'); + } + + static ensureField(obj, name, type) { + if (obj[name] == undefined) { + throw `No '${name}' field on object`; + } + + if (type == 'array') { + if (!Array.isArray(obj[name])) { + throw `${name} is expected to be an array`; + } + return; + } + + if (type) { + const tof = typeof obj[name]; + if (tof !== type) { + throw `Bad type '${tof}' for '${name}'. '${type}' expected.`; + } + } + } + + static isUserCancelled(err) { + return err.error && err.error.code == -32021; + } + + static formatJSON(obj) { + const res = JSON.stringify(obj, null, 2); + return res == '{}' ? obj.toString() : res; + } } - diff --git a/src/app/shared/components/AssetLabel.tsx b/src/app/shared/components/AssetLabel.tsx new file mode 100644 index 0000000..7513f4d --- /dev/null +++ b/src/app/shared/components/AssetLabel.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { styled } from '@linaria/react'; +import AssetIcon from '@app/shared/components/AssetsIcon'; + +interface AssetLabeProps { + title:string, + assets_id: number +} +// TODO: LABEL POSITION +const AssetStyled = styled.div` + max-width: 110px; + width: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + +`; +const Title = styled.h4` + font-style: normal; + font-weight: 400; + font-size: 20px; + line-height: 20px; + color: rgba(255,255,255, 0.7); + text-transform: uppercase; +`; +const AssetLabel = ({ title, assets_id }:AssetLabeProps) => ( + + + {title} + +); + +export default AssetLabel; diff --git a/src/app/shared/components/AssetsContainer.tsx b/src/app/shared/components/AssetsContainer.tsx index 8936a02..353b1ee 100644 --- a/src/app/shared/components/AssetsContainer.tsx +++ b/src/app/shared/components/AssetsContainer.tsx @@ -1,25 +1,24 @@ -import React from "react"; -import { styled } from "@linaria/react"; +import React from 'react'; +import { styled } from '@linaria/react'; + interface SectionProps { variant?: 'center' | 'space-between' } const SectionStyled = styled.div` display: flex; - justify-content: ${({variant}) => variant}; + justify-content: ${({ variant }) => variant}; max-width: 954px; width: 100%; -` +`; const AssetsContainer: React.FC = ({ children, - variant= 'space-between' -}) => { - return ( - - {children} - - ); -}; + variant = 'space-between', +}) => ( + + {children} + +); export default AssetsContainer; diff --git a/src/app/shared/components/AssetsIcon.tsx b/src/app/shared/components/AssetsIcon.tsx new file mode 100644 index 0000000..6445fe9 --- /dev/null +++ b/src/app/shared/components/AssetsIcon.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { BeamIcon as BeamIconSvg, AssetIcon as AssetIconSvg } from '@app/shared/icons'; + +import { styled } from '@linaria/react'; +import { PALLETE_ASSETS } from '@app/shared/constants'; + +export interface AssetIconProps { + asset_id?: number; + className?: string; +} + +const ContainerStyled = styled.div` + display: inline-block; + vertical-align: middle; + width: 26px; + height: 26px; + margin-right: 10px; + color: ${({ asset_id }) => (PALLETE_ASSETS[asset_id] ? PALLETE_ASSETS[asset_id] : PALLETE_ASSETS[asset_id % PALLETE_ASSETS.length])}; +`; + +const AssetIcon: React.FC = ({ asset_id = 0, className }) => { + const IconComponent = asset_id === 0 ? BeamIconSvg : AssetIconSvg; + return ( + + + + ); +}; + +export default AssetIcon; diff --git a/src/app/shared/components/AssetsSection.tsx b/src/app/shared/components/AssetsSection.tsx index d4659a2..6e21cb4 100644 --- a/src/app/shared/components/AssetsSection.tsx +++ b/src/app/shared/components/AssetsSection.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import { styled } from "@linaria/react"; +import React from 'react'; +import { styled } from '@linaria/react'; const SectionStyled = styled.div` display: flex; @@ -13,15 +13,13 @@ const SectionStyled = styled.div` border-radius: 10px; padding-right: 15px; background-color: rgba(255,255,255,0.05); -` +`; const AssetsSection: React.FC = ({ - children, - }) => { - return ( - - {children} - - ); -}; + children, +}) => ( + + {children} + +); export default AssetsSection; diff --git a/src/app/shared/components/BackButton.tsx b/src/app/shared/components/BackButton.tsx index e8b0d48..0ff678e 100644 --- a/src/app/shared/components/BackButton.tsx +++ b/src/app/shared/components/BackButton.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { css, cx } from '@linaria/core'; -import { AngleBackIcon, ArrowLeftIcon } from "@app/shared/icons"; +import { AngleBackIcon, ArrowLeftIcon } from '@app/shared/icons'; import Button from './Button'; @@ -19,11 +19,16 @@ const backStyle = css` const title = css` color:white; margin-left: 10px; -` +`; const BackButton: React.FC = ({ className, onClick, title }) => ( - ); diff --git a/src/app/shared/components/Button.tsx b/src/app/shared/components/Button.tsx index 8655fa4..77fdfa8 100644 --- a/src/app/shared/components/Button.tsx +++ b/src/app/shared/components/Button.tsx @@ -107,7 +107,7 @@ const ControlButton = styled(IconButtonStyled)` margin-right: 16px; vertical-align: middle; } -` +`; const VARIANTS = { regular: ButtonStyled, @@ -115,7 +115,7 @@ const VARIANTS = { link: LinkButtonStyled, icon: IconButtonStyled, block: BlockButtonStyled, - control: ControlButton + control: ControlButton, }; diff --git a/src/app/shared/components/Container.tsx b/src/app/shared/components/Container.tsx index 5bca6af..f383642 100644 --- a/src/app/shared/components/Container.tsx +++ b/src/app/shared/components/Container.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import { styled } from "@linaria/react"; +import React from 'react'; +import { styled } from '@linaria/react'; const ContainerStyled = styled.div` display: flex; @@ -14,13 +14,11 @@ const ContainerStyled = styled.div` `; const Container: React.FC = ({ - children - }) => { - return ( - - {children} - - ); -}; + children, +}) => ( + + {children} + +); export default Container; diff --git a/src/app/shared/components/ReactSelect.tsx b/src/app/shared/components/ReactSelect.tsx index a0b0806..3c16bf7 100644 --- a/src/app/shared/components/ReactSelect.tsx +++ b/src/app/shared/components/ReactSelect.tsx @@ -1,21 +1,42 @@ -import React from "react"; -import Select from "react-select"; -import { IOptions } from "@core/types"; +import React from 'react'; +import Select, { components, Props } from 'react-select'; +import { IOptions } from '@core/types'; +import AssetIcon from '@app/shared/components/AssetsIcon'; -interface IReactSelectProps { - options: IOptions[], - onChange: (any)=>void, - isFilter: boolean, +interface IReactSelectProps extends Props { + options: IOptions[]; + onChange: (any) => void; + isFilter?: boolean; + isIcon?: boolean } -const ReactSelect = ({options, onChange, isFilter, ...rest}:IReactSelectProps) => { +const ReactSelect = ({ + options, onChange, isFilter, isIcon, ...rest +}: IReactSelectProps) => { + const { Option } = components; + function IconOption(props: IOptions) { + const { value, label } = props; + return ( + // TODO: ADD TYPE + // @ts-ignore + + ); + } + return ( -