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

Commit

Permalink
[Claim refactor] Table / InvestmentFlow / Status (#2138)
Browse files Browse the repository at this point in the history
* Refactor: ClaimingStatus

* Refactor: ClaimsTable

* Refactor: InvestmentFlow

* add in new components and remove unused paths

* InvestmentFlow: pass approve props

* comment out broken parts for other PR

* [Claim refactor] Logic changes & connecting real data (#2142)

* hooks index: logic/sdk

- set prices using Price SDK
- useUserEnhancedClaimData sugar
- cleanup / re-org

* utils: mapTypeToPrice

- maps claim types to their respective prices

* types: EnhancedClaimData

* Claim index: pass useEnhancedClaimData hook

* ClaimsTable: pass new data and type

* change back prices to previous

* revert to keep base types same

* fix broken claiming amount representation

* remove unused map

* map > switch

- thanks @alfetopito

* co-authored by @nenadV91
  • Loading branch information
W3stside authored Jan 13, 2022
1 parent f3d027b commit 8ef567f
Show file tree
Hide file tree
Showing 7 changed files with 544 additions and 384 deletions.
70 changes: 70 additions & 0 deletions src/custom/pages/Claim/ClaimingStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Trans } from '@lingui/macro'
import { ConfirmOrLoadingWrapper, ConfirmedIcon, AttemptFooter } from 'pages/Claim/styled'
import { ExternalLink, CustomLightSpinner } from 'theme'
import { ClaimStatus } from 'state/claim/actions'
import { useClaimState } from 'state/claim/hooks'
import { useActiveWeb3React } from 'hooks/web3'
import CowProtocolLogo from 'components/CowProtocolLogo'
import Circle from 'assets/images/blue-loader.svg'
// import { formatSmart } from 'utils/format'

export default function ClaimingStatus() {
const { chainId } = useActiveWeb3React()
const { activeClaimAccount, claimStatus /* , claimedAmount */ } = useClaimState()

// claim status
const isConfirmed = claimStatus === ClaimStatus.CONFIRMED
const isAttempting = claimStatus === ClaimStatus.ATTEMPTING
const isSubmitted = claimStatus === ClaimStatus.SUBMITTED

if (!activeClaimAccount || claimStatus === ClaimStatus.DEFAULT) return null

return (
<ConfirmOrLoadingWrapper activeBG={true}>
<ConfirmedIcon>
{!isConfirmed ? <CustomLightSpinner src={Circle} alt="loader" size={'90px'} /> : <CowProtocolLogo size={100} />}
</ConfirmedIcon>
<h3>{isConfirmed ? 'Claimed!' : 'Claiming'}</h3>
{/* TODO: fix this in new pr */}
{!isConfirmed && <Trans>{/* formatSmart(claimedAmount) || 0 */} vCOW</Trans>}

{isConfirmed && (
<>
<Trans>
<h3>You have successfully claimed</h3>
</Trans>
<Trans>
{/* TODO: fix this in new pr */}
<p>{/* formatSmart(claimedAmount) || 0 */} vCOW</p>
</Trans>
<Trans>
<span role="img" aria-label="party-hat">
🎉🐮{' '}
</span>
Welcome to the COWmunnity! :){' '}
<span role="img" aria-label="party-hat">
🐄🎉
</span>
</Trans>
</>
)}
{isAttempting && (
<AttemptFooter>
<p>
<Trans>Confirm this transaction in your wallet</Trans>
</p>
</AttemptFooter>
)}
{isSubmitted && chainId && (
// && claimTxn?.hash
<ExternalLink
// href={getExplorerLink(chainId, claimTxn?.hash, ExplorerDataType.TRANSACTION)}
href="#"
style={{ zIndex: 99, marginTop: '20px' }}
>
<Trans>View transaction on Explorer</Trans>
</ExternalLink>
)}
</ConfirmOrLoadingWrapper>
)
}
109 changes: 109 additions & 0 deletions src/custom/pages/Claim/ClaimsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { ClaimType, useClaimState } from 'state/claim/hooks'
import { ClaimTable, ClaimBreakdown } from 'pages/Claim/styled'
import CowProtocolLogo from 'components/CowProtocolLogo'
import { ClaimStatus } from 'state/claim/actions'
// import { UserClaimDataDetails } from './types' TODO: fix in another PR
import { formatSmart } from 'utils/format'
import { EnhancedUserClaimData } from './types'

type ClaimsTableProps = {
handleSelectAll: (event: React.ChangeEvent<HTMLInputElement>) => void
handleSelect: (event: React.ChangeEvent<HTMLInputElement>, index: number) => void
userClaimData: EnhancedUserClaimData[]
isAirdropOnly: boolean
hasClaims: boolean
}

// TODO: fix in other pr
type ClaimsTableRowProps = EnhancedUserClaimData &
Pick<ClaimsTableProps, 'handleSelect'> & {
selected: number[]
}

const ClaimsTableRow = ({
index,
type,
isFree,
claimAmount,
currencyAmount,
price,
cost,
handleSelect,
selected,
}: ClaimsTableRowProps) => {
return (
<tr key={index}>
<td>
{' '}
<label className="checkAll">
<input
onChange={(event) => handleSelect(event, index)}
type="checkbox"
name="check"
checked={isFree || selected.includes(index)}
disabled={isFree}
/>
</label>
</td>
<td>{isFree ? ClaimType[type] : `Buy vCOW with ${currencyAmount?.currency?.symbol}`}</td>
<td width="150px">
<CowProtocolLogo size={16} /> {formatSmart(claimAmount) || 0} vCOW
</td>
<td>{isFree || !price ? '-' : `${formatSmart(price) || 0} vCoW per ${currencyAmount?.currency?.symbol}`}</td>
<td>
{isFree ? (
<span className="green">Free!</span>
) : (
`${formatSmart(cost) || 0} ${currencyAmount?.currency?.symbol}`
)}
</td>
<td>{type === ClaimType.Airdrop ? 'No' : '4 years (linear)'}</td>
<td>28 days, 10h, 50m</td>
</tr>
)
}

export default function ClaimsTable({
handleSelectAll,
handleSelect,
userClaimData,
isAirdropOnly,
hasClaims,
}: ClaimsTableProps) {
const { selectedAll, selected, activeClaimAccount, claimStatus, isInvestFlowActive } = useClaimState()

const hideTable =
isAirdropOnly || !hasClaims || !activeClaimAccount || claimStatus !== ClaimStatus.DEFAULT || isInvestFlowActive

if (hideTable) return null

return (
<ClaimBreakdown>
<h2>vCOW claim breakdown</h2>
<ClaimTable>
<table>
<thead>
<tr>
<th>
<label className="checkAll">
<input checked={selectedAll} onChange={handleSelectAll} type="checkbox" name="check" />
</label>
</th>
<th>Type of Claim</th>
<th>Amount</th>
<th>Price</th>
<th>Cost</th>
<th>Vesting</th>
<th>Ends in</th>
</tr>
</thead>
<tbody>
{userClaimData.map((claim: EnhancedUserClaimData) => (
<ClaimsTableRow key={claim.index} {...claim} selected={selected} handleSelect={handleSelect} />
))}
</tbody>
</table>
</ClaimTable>
</ClaimBreakdown>
)
}
216 changes: 216 additions & 0 deletions src/custom/pages/Claim/InvestmentFlow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import {
InvestFlow,
InvestContent,
InvestTokenGroup,
InvestInput,
InvestAvailableBar,
InvestSummary,
InvestFlowValidation,
InvestTokenSubtotal,
StepIndicator,
Steps,
TokenLogo,
} from 'pages/Claim/styled'
import CowProtocolLogo from 'components/CowProtocolLogo'
import { useClaimState } from 'state/claim/hooks'
import { ClaimCommonTypes } from './types'
import { ClaimStatus } from 'state/claim/actions'
import { useActiveWeb3React } from 'hooks/web3'
import { ApprovalState } from 'hooks/useApproveCallback'
import { CheckCircle } from 'react-feather'
import Row from 'components/Row'

type InvestmentFlowProps = Pick<ClaimCommonTypes, 'hasClaims'> & {
isAirdropOnly: boolean
approveState: ApprovalState
approveCallback: () => void
}

export default function InvestmentFlow({
hasClaims,
isAirdropOnly,
approveState,
approveCallback,
}: InvestmentFlowProps) {
const { account } = useActiveWeb3React()

const { activeClaimAccount, claimStatus, isInvestFlowActive, investFlowStep } = useClaimState()

if (
!activeClaimAccount || // no connected account
!hasClaims || // no claims
!isInvestFlowActive || // not on correct step (account change in mid step)
claimStatus !== ClaimStatus.DEFAULT || // not in default claim state
isAirdropOnly // is only for airdrop
) {
return null
}

return (
<InvestFlow>
<StepIndicator>
<h1>
{investFlowStep === 0
? 'Claiming vCOW is a two step process'
: investFlowStep === 1
? 'Set allowance to Buy vCOW'
: 'Confirm transaction to claim all vCOW'}
</h1>
<Steps step={investFlowStep}>
<li>Allowances: Approve all tokens to be used for investment.</li>
<li>Submit and confirm the transaction to claim vCOW</li>
</Steps>
</StepIndicator>

{/* Invest flow: Step 1 > Set allowances and investment amounts */}
{investFlowStep === 1 ? (
<InvestContent>
<p>
Your account can participate in the investment of vCOW. Each investment opportunity will allow you to invest
up to a predefined maximum amount of tokens{' '}
</p>
<InvestTokenGroup>
<div>
<span>
<TokenLogo symbol={'GNO'} size={72} />
<CowProtocolLogo size={72} />
</span>
<h3>Buy vCOW with GNO</h3>
</div>

<span>
<InvestSummary>
<span>
<b>Price</b> <i>16.66 vCoW per GNO</i>
</span>
<span>
<b>Token approval</b>
<i>
{approveState === ApprovalState.NOT_APPROVED ? (
'GNO not approved'
) : (
<Row>
GNO approved <CheckCircle color="lightgreen" style={{ marginLeft: 5 }} />
</Row>
)}
</i>
{approveState === ApprovalState.NOT_APPROVED && (
<button onClick={approveCallback}>Approve GNO</button>
)}
</span>
<span>
<b>Max. investment available</b> <i>2,500.04 GNO</i>
</span>
<span>
<b>Available investment used</b> <InvestAvailableBar percentage={50} />
</span>
</InvestSummary>
<InvestInput>
<div>
<span>
<b>Balance:</b> <i>10,583.34 GNO</i>
{/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */}
<button>Invest max. possible</button>
</span>
<label>
<b>GNO</b>
<input placeholder="0" />
</label>
<i>Receive: 32,432.54 vCOW</i>
{/* Insufficient balance validation error */}
<small>
Insufficient balance to invest. Adjust the amount or go back to remove this investment option.
</small>
</div>
</InvestInput>
</span>
</InvestTokenGroup>

<InvestTokenGroup>
<div>
<span>
<TokenLogo symbol={'ETH'} size={72} />
<CowProtocolLogo size={72} />
</span>
<h3>Buy vCOW with ETH</h3>
</div>

<span>
<InvestSummary>
<span>
<b>Price</b> <i>16.66 vCoW per ETH</i>
</span>
<span>
<b>Token approval</b>
<i>
<Row>
Not required for ETH! <CheckCircle color="lightgreen" style={{ marginLeft: 5 }} />
</Row>
</i>
</span>
<span>
<b>Max. investment available</b> <i>2,500.04 ETH</i>
</span>
<span>
<b>Available investment used</b> <InvestAvailableBar percentage={50} />
</span>
</InvestSummary>
<InvestInput>
<div>
<span>
<b>Balance:</b> <i>10,583.34 ETH</i>
{/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */}
<button>Invest max. possible</button>
</span>
<label>
<b>ETH</b>
<input placeholder="0" />
</label>
<i>Receive: 32,432.54 vCOW</i>
{/* Insufficient balance validation error */}
<small>
Insufficient balance to invest. Adjust the amount or go back to remove this investment option.
</small>
</div>
</InvestInput>
</span>
</InvestTokenGroup>

<InvestTokenSubtotal>
{activeClaimAccount} will receive: 4,054,671.28 vCOW based on investment(s)
</InvestTokenSubtotal>

<InvestFlowValidation>Approve all investment tokens before continuing</InvestFlowValidation>
</InvestContent>
) : null}

{/* Invest flow: Step 2 > Review summary */}
{investFlowStep === 2 ? (
<InvestContent>
1. Claim airdrop: {activeClaimAccount} receives 13,120.50 vCOW (Note: please make sure you intend to claim and
send vCOW to the mentioned account)
<br />
<br />
2. Claim and invest: Investing with account: {account} (connected account). Investing: 1343 GNO (50% of
available investing opportunity) and 32 ETH (30% of available investing opportunity)
<br />
<br />
3. Receive vCOW claims on account {activeClaimAccount}: 23,947.6 vCOW - available NOW! and 120,567.12 vCOW -
Vested linearly 4 years <br />
<br />
<br />
<h4>Ready to claim your vCOW?</h4>
<p>
<b>What will happen?</b> By sending this Ethereum transaction, you will be investing tokens from the
connected account and exchanging them for vCOW tokens that will be received by the claiming account
specified above.
</p>
<p>
<b>Can I modify the invested amounts or invest partial amounts later?</b> No. Once you send the transaction,
you cannot increase or reduce the investment. Investment oportunities can only be exercised once.
</p>
</InvestContent>
) : null}
</InvestFlow>
)
}
Loading

0 comments on commit 8ef567f

Please sign in to comment.