Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Claim investment flow 2 #2163

Merged
merged 8 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 44 additions & 29 deletions src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
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'
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'
Expand All @@ -29,40 +30,61 @@ 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<Currency>, 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<string>(INVESTMENT_STEPS[0])

const { account } = useActiveWeb3React()

const token = currencyAmount?.currency

const balance = useCurrencyBalance(account || undefined, token)

const handlePercentChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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]
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discussed yesterday, we won't need this (unles u want it for set max amount), which i guess would be overkill


const onMaxClick = useCallback(() => {
if (!maxCost || !balance) {
return
}

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])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what a elaborate way to just set the percentage to 100%

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hehe that's my bad :P we did this together on the call

}, [balance, maxCost, optionIndex, updateInvestAmount])

// Cache approveData methods
const approveCallback = approveData?.approveCallback
Expand All @@ -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])

Expand Down Expand Up @@ -162,21 +184,14 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }:

<div>
<RangeSteps>
{INVESTMENT_STEPS.map((step: number) => (
{INVESTMENT_STEPS.map((step: string) => (
<RangeStep onClick={() => handleStepChange(step)} key={step}>
{step}%
</RangeStep>
))}
</RangeSteps>

<input
style={{ width: '100%' }}
onChange={handlePercentChange}
type="range"
min="0"
max="100"
value={0}
/>
<InvestAvailableBar percentage={Number(percentage)} />
</div>
</span>
</InvestSummary>
Expand All @@ -194,7 +209,7 @@ export default function InvestOption({ approveData, updateInvestAmount, claim }:
<input
// disabled
placeholder="0"
value={investedAmount}
value={investedAmount ? formatUnits(investedAmount, decimals) : '0'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this field be handled if it changes?
It can be done in another PR

max={formatSmart(currencyAmount)}
/>
<b>{currencyAmount?.currency?.symbol}</b>
Expand Down
47 changes: 14 additions & 33 deletions src/custom/pages/Claim/InvestmentFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState, useCallback } from 'react'
import { useEffect, useMemo } from 'react'
import {
InvestFlow,
InvestContent,
Expand All @@ -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
Expand Down Expand Up @@ -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<InvestmentClaimProps[]>([])
const selectedClaims = useMemo(() => {
return claimData.filter(({ index }) => selected.includes(index))
}, [claimData, selected])

useEffect(() => {
if (claimData) {
const data = claimData.reduce<InvestmentClaimProps[]>((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
Expand Down Expand Up @@ -113,11 +94,11 @@ export default function InvestmentFlow({ hasClaims, isAirdropOnly, ...tokenAppro
up to a predefined maximum amount of tokens{' '}
</p>

{investData.map((claim) => (
{selectedClaims.map((claim, index) => (
<InvestOption
key={claim.index}
optionIndex={index}
approveData={_claimToTokenApproveData(claim.type, tokenApproveData)}
updateInvestAmount={updateInvestAmount}
claim={claim}
/>
))}
Expand Down
12 changes: 9 additions & 3 deletions src/custom/pages/Claim/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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%);
}
`

Expand Down
11 changes: 11 additions & 0 deletions src/custom/state/claim/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -40,6 +46,11 @@ export const setClaimStatus = createAction<ClaimStatus>('claim/setClaimStatus')
// investing
export const setIsInvestFlowActive = createAction<boolean>('claim/setIsInvestFlowActive')
export const setInvestFlowStep = createAction<number>('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<number[]>('claim/setSelected')
Expand Down
4 changes: 4 additions & 0 deletions src/custom/state/claim/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import {
setClaimedAmount,
setIsInvestFlowActive,
setInvestFlowStep,
initInvestFlowData,
updateInvestAmount,
setSelected,
setSelectedAll,
ClaimStatus,
Expand Down Expand Up @@ -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)),
Expand Down
27 changes: 26 additions & 1 deletion src/custom/state/claim/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createReducer } from '@reduxjs/toolkit'
import { createReducer, current } from '@reduxjs/toolkit'
import {
setActiveClaimAccount,
setActiveClaimAccountENS,
Expand All @@ -7,6 +7,8 @@ import {
setInputAddress,
setInvestFlowStep,
setIsInvestFlowActive,
initInvestFlowData,
updateInvestAmount,
setIsSearchUsed,
setSelected,
setSelectedAll,
Expand All @@ -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
Expand All @@ -46,6 +56,7 @@ export type ClaimState = {
// investment
isInvestFlowActive: boolean
investFlowStep: number
investFlowData: InvestClaim[]
// table select change
selected: number[]
selectedAll: boolean
Expand Down Expand Up @@ -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
})
Expand Down