diff --git a/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx index cc8951628..765e7c983 100644 --- a/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx +++ b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx @@ -1,10 +1,10 @@ import { useCallback, useMemo, useState } from 'react' import styled from 'styled-components/macro' import CowProtocolLogo from 'components/CowProtocolLogo' -import { formatUnits, parseUnits } from '@ethersproject/units' -import { CurrencyAmount } from '@uniswap/sdk-core' +import { formatUnits } from '@ethersproject/units' +import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' -import { InvestTokenGroup, TokenLogo, InvestSummary, InvestInput } from '../styled' +import { InvestTokenGroup, TokenLogo, InvestSummary, InvestInput, InvestAvailableBar } from '../styled' import { formatSmart } from 'utils/format' import Row from 'components/Row' import { CheckCircle } from 'react-feather' @@ -12,6 +12,7 @@ import { InvestOptionProps } from '.' import { ApprovalState } from 'hooks/useApproveCallback' import { useCurrencyBalance } from 'state/wallet/hooks' import { useActiveWeb3React } from 'hooks/web3' +import { useClaimDispatchers, useClaimState } from 'state/claim/hooks' import { ButtonConfirmed } from 'components/Button' import { ButtonSize } from 'theme' @@ -29,14 +30,28 @@ const RangeStep = styled.button` border: none; font-size: 0.8rem; cursor: pointer; - color: blue; + color: ${({ theme }) => theme.primary1}; padding: 0; ` -const INVESTMENT_STEPS = [0, 25, 50, 75, 100] +const INVESTMENT_STEPS = ['0', '25', '50', '75', '100'] -export default function InvestOption({ approveData, updateInvestAmount, claim }: InvestOptionProps) { - const { currencyAmount, price, cost: maxCost, investedAmount } = claim +function _scaleValue(maxValue: CurrencyAmount, value: string) { + // parse percent to string, example 25% -> 4 or 50% -> 2 + const parsedValue = new Fraction(value, '100') + + // divide maxValue with parsed value to get invest amount + return maxValue.multiply(parsedValue).asFraction +} + +export default function InvestOption({ approveData, claim, optionIndex }: InvestOptionProps) { + const { currencyAmount, price, cost: maxCost } = claim + const { updateInvestAmount } = useClaimDispatchers() + const { investFlowData } = useClaimState() + + const investedAmount = useMemo(() => investFlowData[optionIndex].investedAmount, [investFlowData, optionIndex]) + + const [percentage, setPercentage] = useState(INVESTMENT_STEPS[0]) const { account } = useActiveWeb3React() @@ -44,13 +59,21 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }: const balance = useCurrencyBalance(account || undefined, token) - const handlePercentChange = (event: React.ChangeEvent) => { - console.log(event.target.value) - } + const decimals = balance?.currency?.decimals - const handleStepChange = (value: number) => { - console.log(value) - } + const handleStepChange = useCallback( + (value: string) => { + if (!maxCost || !balance) { + return + } + + const scaledCurrencyAmount = _scaleValue(maxCost, value) + + updateInvestAmount({ index: optionIndex, amount: scaledCurrencyAmount.quotient.toString() }) + setPercentage(value) + }, + [balance, maxCost, optionIndex, updateInvestAmount] + ) const onMaxClick = useCallback(() => { if (!maxCost || !balance) { @@ -58,11 +81,10 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }: } const amount = maxCost.greaterThan(balance) ? balance : maxCost - // store the value as a string to prevent unnecessary re-renders - const investAmount = formatUnits(amount.quotient.toString(), balance.currency.decimals) - updateInvestAmount(claim.index, investAmount) - }, [balance, claim.index, maxCost, updateInvestAmount]) + updateInvestAmount({ index: optionIndex, amount: amount.quotient.toString() }) + setPercentage(INVESTMENT_STEPS[INVESTMENT_STEPS.length - 1]) + }, [balance, maxCost, optionIndex, updateInvestAmount]) // Cache approveData methods const approveCallback = approveData?.approveCallback @@ -84,11 +106,11 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }: }, [approveCallback, token?.symbol]) const vCowAmount = useMemo(() => { - if (!token || !price) { + if (!token || !price || !investedAmount) { return } - const investA = CurrencyAmount.fromRawAmount(token, parseUnits(investedAmount, token.decimals).toString()) + const investA = CurrencyAmount.fromRawAmount(token, investedAmount) return investA.multiply(price) }, [investedAmount, price, token]) @@ -162,21 +184,14 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }:
- {INVESTMENT_STEPS.map((step: number) => ( + {INVESTMENT_STEPS.map((step: string) => ( handleStepChange(step)} key={step}> {step}% ))} - +
@@ -194,7 +209,7 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }: {currencyAmount?.currency?.symbol} diff --git a/src/custom/pages/Claim/InvestmentFlow/index.tsx b/src/custom/pages/Claim/InvestmentFlow/index.tsx index 571ad517c..14ea94758 100644 --- a/src/custom/pages/Claim/InvestmentFlow/index.tsx +++ b/src/custom/pages/Claim/InvestmentFlow/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useCallback } from 'react' +import { useEffect, useMemo } from 'react' import { InvestFlow, InvestContent, @@ -7,20 +7,16 @@ import { StepIndicator, Steps, } from 'pages/Claim/styled' -import { ClaimType, useClaimState, useUserEnhancedClaimData } from 'state/claim/hooks' +import { ClaimType, useClaimState, useUserEnhancedClaimData, useClaimDispatchers } from 'state/claim/hooks' import { ClaimCommonTypes, EnhancedUserClaimData } from '../types' import { ClaimStatus } from 'state/claim/actions' import { useActiveWeb3React } from 'hooks/web3' import { ApprovalState, OptionalApproveCallbackParams } from 'hooks/useApproveCallback' import InvestOption from './InvestOption' -export type InvestmentClaimProps = EnhancedUserClaimData & { - investedAmount: string -} - export type InvestOptionProps = { - claim: InvestmentClaimProps - updateInvestAmount: (idx: number, investAmount: string) => void + claim: EnhancedUserClaimData + optionIndex: number approveData: | { approveState: ApprovalState; approveCallback: (optionalParams?: OptionalApproveCallbackParams) => void } | undefined @@ -51,33 +47,18 @@ function _claimToTokenApproveData(claimType: ClaimType, tokenApproveData: TokenA export default function InvestmentFlow({ hasClaims, isAirdropOnly, ...tokenApproveData }: InvestmentFlowProps) { const { account } = useActiveWeb3React() - const { activeClaimAccount, claimStatus, isInvestFlowActive, investFlowStep, selected } = useClaimState() - + const { selected, activeClaimAccount, claimStatus, isInvestFlowActive, investFlowStep } = useClaimState() + const { initInvestFlowData } = useClaimDispatchers() const claimData = useUserEnhancedClaimData(activeClaimAccount) - const [investData, setInvestData] = useState([]) + const selectedClaims = useMemo(() => { + return claimData.filter(({ index }) => selected.includes(index)) + }, [claimData, selected]) useEffect(() => { - if (claimData) { - const data = claimData.reduce((acc, claim) => { - if (selected.includes(claim.index)) { - acc.push({ ...claim, investedAmount: '0' }) - } - - return acc - }, []) - - setInvestData(data) - } - }, [selected, claimData]) - - const updateInvestAmount = useCallback( - (idx: number, investedAmount: string) => { - const update = investData.map((claim) => (claim.index === idx ? { ...claim, investedAmount } : claim)) - setInvestData(update) - }, - [investData] - ) + initInvestFlowData() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isInvestFlowActive]) if ( !activeClaimAccount || // no connected account @@ -113,11 +94,11 @@ export default function InvestmentFlow({ hasClaims, isAirdropOnly, ...tokenAppro up to a predefined maximum amount of tokens{' '}

- {investData.map((claim) => ( + {selectedClaims.map((claim, index) => ( ))} diff --git a/src/custom/pages/Claim/styled.ts b/src/custom/pages/Claim/styled.ts index c57d8e40e..2d921bdf5 100644 --- a/src/custom/pages/Claim/styled.ts +++ b/src/custom/pages/Claim/styled.ts @@ -917,17 +917,23 @@ export const InvestAvailableBar = styled.div<{ percentage?: number }>` &::before { content: ''; display: block; - background: color: ${({ theme }) => theme.primary1}; + background-color: ${({ theme }) => theme.primary1}; height: 100%; border-radius: 24px; + position: absolute; + left: 0; + top: 0; + bottom: 0; + transition: width 0.3s ease-in; width: ${({ percentage }) => (percentage ? `${percentage}%` : '0%')}; } &::after { content: ${({ percentage }) => (percentage ? `'${percentage}%'` : '0%')}; display: inline-block; - font-size: 13px; - color: ${({ theme }) => theme.primary1}; + font-size: 11px; + color: white; + transform: translate(-120%, -20%); } ` diff --git a/src/custom/state/claim/actions.ts b/src/custom/state/claim/actions.ts index d6152753d..47fbcc4ab 100644 --- a/src/custom/state/claim/actions.ts +++ b/src/custom/state/claim/actions.ts @@ -12,14 +12,20 @@ export type ClaimActions = { setInputAddress: (payload: string) => void setActiveClaimAccount: (payload: string) => void setActiveClaimAccountENS: (payload: string) => void + // search setIsSearchUsed: (payload: boolean) => void + // claiming setClaimStatus: (payload: ClaimStatus) => void setClaimedAmount: (payload: number) => void + // investing setIsInvestFlowActive: (payload: boolean) => void setInvestFlowStep: (payload: number) => void + initInvestFlowData: () => void + updateInvestAmount: (payload: { index: number; amount: string }) => void + // claim row selection setSelected: (payload: number[]) => void setSelectedAll: (payload: boolean) => void @@ -40,6 +46,11 @@ export const setClaimStatus = createAction('claim/setClaimStatus') // investing export const setIsInvestFlowActive = createAction('claim/setIsInvestFlowActive') export const setInvestFlowStep = createAction('claim/setInvestFlowStep') +export const initInvestFlowData = createAction('claim/initInvestFlowData') +export const updateInvestAmount = createAction<{ + index: number + amount: string +}>('claim/updateInvestAmount') // claim row selection export const setSelected = createAction('claim/setSelected') diff --git a/src/custom/state/claim/hooks/index.ts b/src/custom/state/claim/hooks/index.ts index 030ecacfb..75c034a53 100644 --- a/src/custom/state/claim/hooks/index.ts +++ b/src/custom/state/claim/hooks/index.ts @@ -46,6 +46,8 @@ import { setClaimedAmount, setIsInvestFlowActive, setInvestFlowStep, + initInvestFlowData, + updateInvestAmount, setSelected, setSelectedAll, ClaimStatus, @@ -723,6 +725,8 @@ export function useClaimDispatchers() { // investing setIsInvestFlowActive: (payload: boolean) => dispatch(setIsInvestFlowActive(payload)), setInvestFlowStep: (payload: number) => dispatch(setInvestFlowStep(payload)), + initInvestFlowData: () => dispatch(initInvestFlowData()), + updateInvestAmount: (payload: { index: number; amount: string }) => dispatch(updateInvestAmount(payload)), // claim row selection setSelected: (payload: number[]) => dispatch(setSelected(payload)), setSelectedAll: (payload: boolean) => dispatch(setSelectedAll(payload)), diff --git a/src/custom/state/claim/reducer.ts b/src/custom/state/claim/reducer.ts index 5f26481e6..3f8c42e9c 100644 --- a/src/custom/state/claim/reducer.ts +++ b/src/custom/state/claim/reducer.ts @@ -1,4 +1,4 @@ -import { createReducer } from '@reduxjs/toolkit' +import { createReducer, current } from '@reduxjs/toolkit' import { setActiveClaimAccount, setActiveClaimAccountENS, @@ -7,6 +7,8 @@ import { setInputAddress, setInvestFlowStep, setIsInvestFlowActive, + initInvestFlowData, + updateInvestAmount, setIsSearchUsed, setSelected, setSelectedAll, @@ -27,11 +29,19 @@ export const initialState: ClaimState = { // investment isInvestFlowActive: false, investFlowStep: 0, + investFlowData: [], // table select change selected: [], selectedAll: false, } +export type InvestClaim = { + index: number + inputAmount?: string + investedAmount?: string + vCowAmount?: string +} + export type ClaimState = { // address/ENS address inputAddress: string @@ -46,6 +56,7 @@ export type ClaimState = { // investment isInvestFlowActive: boolean investFlowStep: number + investFlowData: InvestClaim[] // table select change selected: number[] selectedAll: boolean @@ -77,6 +88,20 @@ export default createReducer(initialState, (builder) => .addCase(setInvestFlowStep, (state, { payload }) => { state.investFlowStep = payload }) + .addCase(initInvestFlowData, (state) => { + const { selected, isInvestFlowActive } = current(state) + + const data = selected.map((index) => ({ index, investedAmount: '0' })) + + if (isInvestFlowActive) { + state.investFlowData.push(...data) + } else { + state.investFlowData.length = 0 + } + }) + .addCase(updateInvestAmount, (state, { payload: { index, amount } }) => { + state.investFlowData[index].investedAmount = amount + }) .addCase(setSelected, (state, { payload }) => { state.selected = payload })