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

Commit

Permalink
Merge branch 'claim' into claim-investment-flow-2
Browse files Browse the repository at this point in the history
  • Loading branch information
fairlighteth committed Jan 18, 2022
2 parents 46410cc + 325efc1 commit d90b526
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 52 deletions.
62 changes: 62 additions & 0 deletions src/custom/components/ProgressBar/index.tsx
@@ -0,0 +1,62 @@
import { ProgressBarWrap, ProgressContainer, Progress, Label, FlexWrap, HiddenRange, ProgressVal } from './styled'

interface ProgressBarProps {
percentage: number // between 0 - 100
onPercentageClick: (percentage: number) => void
}

export function ProgressBar({ percentage, onPercentageClick }: ProgressBarProps) {
const statPercentages = [
{
value: 0,
label: '0%',
},
{
value: 25,
label: '25%',
},
{
value: 50,
label: '50%',
},
{
value: 75,
label: '75%',
},
{
value: 100,
label: '100%',
},
]
const minVal = statPercentages[0].value
const maxVal = statPercentages[statPercentages.length - 1].value

if (percentage > 100) {
percentage = 100
} else if (percentage < 0) {
percentage = 0
}

return (
<FlexWrap>
<ProgressBarWrap>
{statPercentages.map((item, index) => (
<Label position={item.value} onClick={() => onPercentageClick(item.value)} key={`${item.value}-${index}`}>
{item.label}
</Label>
))}
<ProgressContainer>
<HiddenRange
onChange={(e) => onPercentageClick(parseFloat(e.target.value))}
min={minVal}
max={maxVal}
value={percentage}
type="range"
/>
<Progress percentage={percentage} />
<ProgressVal>{percentage}%</ProgressVal>
</ProgressContainer>
</ProgressBarWrap>
</FlexWrap>
)
}
77 changes: 77 additions & 0 deletions src/custom/components/ProgressBar/styled.tsx
@@ -0,0 +1,77 @@
import styled from 'styled-components/macro'
import * as CSS from 'csstype'
import { FlexWrap as FlexWrapMod } from 'pages/Profile/styled'
import { transparentize } from 'polished'

export const FlexWrap = styled(FlexWrapMod)`
max-width: 100%;
align-items: flex-end;
`

export const ProgressBarWrap = styled(FlexWrapMod)`
max-width: 500px; //optional
padding-top: 40px;
position: relative;
`

export const ProgressContainer = styled.div`
background-color: ${({ theme }) => transparentize(0.61, theme.text1)};
height: 24px;
width: 100% !important;
position: relative;
overflow: hidden;
border-radius: 10px;
`

export const HiddenRange = styled.input`
width: 100%;
background-color: transparent;
z-index: 3;
position: relative;
opacity: 0;
`

export const Progress = styled.div<Partial<CSS.Properties & { percentage: number }>>`
background-color: ${({ theme }) => theme.primary1};
width: 100%;
max-width: ${(props) => props.percentage}%;
position: absolute;
top: 0;
left: 0;
bottom: 0;
transition: max-width 0.2s;
`

export const ProgressVal = styled.span`
display: inline-block;
color: ${({ theme }) => theme.text1};
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
font-weight: bold;
font-size: 16px;
`

export const Label = styled.a<Partial<CSS.Properties & { position: any }>>`
cursor: pointer;
position: absolute;
font-size: 12px;
color: ${({ theme }) => theme.text1};
top: 10px;
left: ${(props) => props.position}%;
transform: translateX(-50%);
font-weight: bold;
text-decoration: underline;
&:first-child {
transform: none;
}
&:nth-last-child(2) {
transform: translateX(-100%);
}
&:hover {
text-decoration: none;
}
`
45 changes: 34 additions & 11 deletions src/custom/pages/Claim/ClaimsTable.tsx
@@ -1,12 +1,21 @@
import {
ClaimType,
useAirdropDeadline,
useClaimState,
useDeploymentTimestamp,
useInvestmentDeadline,
} from 'state/claim/hooks'
import styled from 'styled-components/macro'
import { ClaimType, useClaimState } from 'state/claim/hooks'
import { ClaimTable, ClaimBreakdown, TokenLogo } 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'
import { useAllClaimingTransactionIndices } from 'state/enhancedTransactions/hooks'
import { CustomLightSpinner } from 'theme'
import Circle from 'assets/images/blue-loader.svg'
import { Countdown } from 'pages/Claim/Countdown'

type ClaimsTableProps = {
handleSelectAll: (event: React.ChangeEvent<HTMLInputElement>) => void
Expand All @@ -20,6 +29,8 @@ type ClaimsTableProps = {
type ClaimsTableRowProps = EnhancedUserClaimData &
Pick<ClaimsTableProps, 'handleSelect'> & {
selected: number[]
start: number | null
end: number | null
isPendingClaim: boolean
}

Expand Down Expand Up @@ -48,28 +59,34 @@ const ClaimsTableRow = ({
cost,
handleSelect,
selected,
start,
end,
}: ClaimsTableRowProps) => {
return (
<ClaimTr key={index} isPending={isPendingClaim}>
<td>
{' '}
<label className="checkAll">
<input
onChange={(event) => handleSelect(event, index)}
type="checkbox"
name="check"
checked={isFree || selected.includes(index)}
disabled={isFree}
/>
{isPendingClaim ? (
<CustomLightSpinner src={Circle} title="Claiming in progress..." alt="loader" size="24px" />
) : (
<input
onChange={(event) => handleSelect(event, index)}
type="checkbox"
name="check"
checked={isFree || selected.includes(index)}
disabled={isFree}
/>
)}
</label>
</td>
<td>
{' '}
<TokenLogo symbol={`${currencyAmount?.currency?.symbol}`} size={32} />
<CowProtocolLogo size={32} />
<span>
<b>Buy vCOW</b>
<i>{isFree ? ClaimType[type] : `with ${currencyAmount?.currency?.symbol}`}</i>
<b>{isFree ? ClaimType[type] : 'Buy vCOW'}</b>
{!isFree && <i>with {currencyAmount?.currency?.symbol}</i>}
</span>
</td>
<td>{formatSmart(claimAmount) || 0} vCOW</td>
Expand All @@ -93,7 +110,7 @@ const ClaimsTableRow = ({
Vesting: <b>{type === ClaimType.Airdrop ? 'No' : '4 years (linear)'}</b>
</span>
<span>
Ends in: <b>28 days, 10h, 50m</b>
Ends in: <b>{start && end && <Countdown start={start} end={end} />}</b>
</span>
</td>
</ClaimTr>
Expand All @@ -113,6 +130,10 @@ export default function ClaimsTable({
const hideTable =
isAirdropOnly || !hasClaims || !activeClaimAccount || claimStatus !== ClaimStatus.DEFAULT || isInvestFlowActive

const start = useDeploymentTimestamp()
const investmentEnd = useInvestmentDeadline()
const airdropEnd = useAirdropDeadline()

if (hideTable) return null

return (
Expand Down Expand Up @@ -140,6 +161,8 @@ export default function ClaimsTable({
isPendingClaim={pendingClaimsSet.has(claim.index)}
selected={selected}
handleSelect={handleSelect}
start={start}
end={claim.isFree ? airdropEnd : investmentEnd}
/>
))}
</tbody>
Expand Down
67 changes: 67 additions & 0 deletions src/custom/pages/Claim/Countdown.tsx
@@ -0,0 +1,67 @@
// Sort of a mod of but not quite from src/pages/Earn/Countdown.tsx
import { useEffect, useState } from 'react'

const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24

export type Props = {
start: number
end: number
}

/**
* Copied over from src/pages/Earn/Countdown.tsx and heavily modified it
*
* If current time is past end time, returns null
*
* @param start start time in ms
* @param end end time in ms
*/
export function Countdown({ start, end }: Props) {
// get current time, store as seconds because 🤷
const [time, setTime] = useState(() => Math.floor(Date.now() / 1000))

useEffect((): (() => void) | void => {
// we only need to tick if not ended yet
if (time <= end / 1000) {
const timeout = setTimeout(() => setTime(Math.floor(Date.now() / 1000)), 1000)
return () => {
clearTimeout(timeout)
}
}
}, [time, end])

const timeUntilGenesis = start / 1000 - time
const timeUntilEnd = end / 1000 - time

let timeRemaining: number
if (timeUntilGenesis >= 0) {
timeRemaining = timeUntilGenesis
} else {
const ongoing = timeUntilEnd >= 0
if (ongoing) {
timeRemaining = timeUntilEnd
} else {
timeRemaining = Infinity
}
}

const days = (timeRemaining - (timeRemaining % DAY)) / DAY
timeRemaining -= days * DAY
const hours = (timeRemaining - (timeRemaining % HOUR)) / HOUR
timeRemaining -= hours * HOUR
const minutes = (timeRemaining - (timeRemaining % MINUTE)) / MINUTE
timeRemaining -= minutes * MINUTE
const seconds = timeRemaining

return (
<>
{Number.isFinite(timeRemaining)
? `${days} days, ${hours.toString().padStart(2, '0')}h, ${minutes.toString().padStart(2, '0')}m, ${seconds
.toString()
.padStart(2, '0')}s`
: 'No longer claimable'}
</>
)
}
23 changes: 13 additions & 10 deletions src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
Expand Up @@ -144,11 +144,11 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
return (
<InvestTokenGroup>
<div>
<h3>Buy vCOW with {currencyAmount?.currency?.symbol}</h3>
<span>
<TokenLogo symbol={currencyAmount?.currency?.symbol || '-'} size={72} />
<CowProtocolLogo size={72} />
</span>
<h3>Buy vCOW with {currencyAmount?.currency?.symbol}</h3>
</div>

<span>
Expand All @@ -159,6 +159,14 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
{formatSmart(price)} vCoW per {currencyAmount?.currency?.symbol}
</i>
</span>

<span>
<b>Max. investment available</b>{' '}
<i>
{formatSmart(maxCost) || '0'} {currencyAmount?.currency?.symbol}
</i>
</span>

<span>
<b>Token approval</b>
{approveData ? (
Expand All @@ -180,7 +188,7 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
</i>
)}
{/* Approve button - @biocom styles for this found in ./styled > InputSummary > ${ButtonPrimary}*/}
{approveState !== ApprovalState.APPROVED && (
{approveData && approveState !== ApprovalState.APPROVED && (
<ButtonConfirmed
buttonSize={ButtonSize.SMALL}
onClick={handleApprove}
Expand All @@ -191,18 +199,13 @@ export default function InvestOption({ approveData, claim, optionIndex }: Invest
>
{approving || approveState === ApprovalState.PENDING ? (
<Loader stroke="white" />
) : (
) : approveData ? (
<span>Approve {currencyAmount?.currency?.symbol}</span>
)}
) : null}
</ButtonConfirmed>
)}
</span>
<span>
<b>Max. investment available</b>{' '}
<i>
{formatSmart(maxCost) || '0'} {currencyAmount?.currency?.symbol}
</i>
</span>

<span>
<b>Available investment used</b>

Expand Down

0 comments on commit d90b526

Please sign in to comment.