-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* renewal refactor * lint & format * info cleanup * format & improvements * improvements & cleanup * move to components * fix * format core
- Loading branch information
Showing
11 changed files
with
393 additions
and
300 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { Stack } from '@mui/material'; | ||
import { useState } from 'react'; | ||
|
||
import { RenewableParachain } from '@/hooks'; | ||
import { useSubmitExtrinsic } from '@/hooks/submitExtrinsic'; | ||
|
||
import { ProgressButton } from '@/components'; | ||
|
||
import { useAccounts } from '@/contexts/account'; | ||
import { useCoretimeApi } from '@/contexts/apis'; | ||
import { useToast } from '@/contexts/toast'; | ||
|
||
interface RenewActionProps { | ||
parachain: RenewableParachain; | ||
enabled: boolean; | ||
} | ||
|
||
export const RenewAction = ({ parachain, enabled }: RenewActionProps) => { | ||
const [working, setWorking] = useState(false); | ||
|
||
const { | ||
state: { activeAccount, activeSigner }, | ||
} = useAccounts(); | ||
const { | ||
state: { api: coretimeApi, isApiReady: isCoretimeReady, decimals, symbol }, | ||
} = useCoretimeApi(); | ||
|
||
const { toastError, toastInfo, toastSuccess } = useToast(); | ||
const { submitExtrinsicWithFeeInfo } = useSubmitExtrinsic(); | ||
|
||
const handleRenew = () => { | ||
if (!activeAccount || !coretimeApi || !isCoretimeReady || !activeSigner) return; | ||
|
||
const { core } = parachain; | ||
|
||
const txRenewal = coretimeApi.tx.broker.renew(core); | ||
submitExtrinsicWithFeeInfo(symbol, decimals, txRenewal, activeAccount.address, activeSigner, { | ||
ready: () => { | ||
setWorking(true); | ||
toastInfo('Transaction was initiated'); | ||
}, | ||
inBlock: () => toastInfo('In Block'), | ||
finalized: () => setWorking(false), | ||
success: () => { | ||
toastSuccess('Successfully renewed the selected parachain'); | ||
}, | ||
fail: () => { | ||
toastError(`Failed to renew the selected parachain`); | ||
}, | ||
error: (e) => { | ||
toastError(`Failed to renew the selected parachain ${e}`); | ||
setWorking(false); | ||
}, | ||
}); | ||
}; | ||
|
||
return ( | ||
<> | ||
<Stack direction='row' gap='1rem' marginTop='2em' justifyContent='center'> | ||
<ProgressButton | ||
disabled={!enabled} | ||
label='Renew' | ||
onClick={handleRenew} | ||
loading={working} | ||
width='200px' | ||
/> | ||
</Stack> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { Box, Stack, Tooltip, Typography } from '@mui/material'; | ||
import { humanizer } from 'humanize-duration'; | ||
import { Dispatch, SetStateAction, useEffect, useState } from 'react'; | ||
|
||
import { RenewableParachain } from '@/hooks'; | ||
import { getBalanceString, timesliceToTimestamp } from '@/utils/functions'; | ||
import theme from '@/utils/muiTheme'; | ||
|
||
import { Banner } from '@/components'; | ||
|
||
import { useCoretimeApi, useRelayApi } from '@/contexts/apis'; | ||
import { useSaleInfo } from '@/contexts/sales'; | ||
import { ContextStatus } from '@/models'; | ||
|
||
interface RenewableParaInfoProps { | ||
parachain: RenewableParachain; | ||
setRenewalEnabled: Dispatch<SetStateAction<boolean>>; | ||
} | ||
|
||
export const RenewableParaInfo = ({ parachain, setRenewalEnabled }: RenewableParaInfoProps) => { | ||
const [expiryTimestamp, setExpiryTimestamp] = useState(0); | ||
|
||
const { saleInfo, saleStatus, status: saleInfoStatus, phase } = useSaleInfo(); | ||
|
||
const { | ||
state: { api: relayApi, isApiReady: isRelayReady }, | ||
} = useRelayApi(); | ||
const { | ||
state: { api: coretimeApi, isApiReady: isCoretimeReady }, | ||
timeslicePeriod, | ||
} = useCoretimeApi(); | ||
|
||
const [loading, setLoading] = useState(false); | ||
|
||
useEffect(() => { | ||
const getExpiry = async () => { | ||
setLoading(true); | ||
if ( | ||
!coretimeApi || | ||
!isCoretimeReady || | ||
!relayApi || | ||
!isRelayReady || | ||
saleInfoStatus !== ContextStatus.LOADED | ||
) | ||
return; | ||
|
||
const now = await timesliceToTimestamp( | ||
relayApi, | ||
saleStatus.lastCommittedTimeslice, | ||
timeslicePeriod | ||
); | ||
const expiry = await timesliceToTimestamp(relayApi, parachain.when, timeslicePeriod); | ||
|
||
if (expiry - now < 0) { | ||
setExpiryTimestamp(phase.endpoints.fixed.end - now); | ||
} else { | ||
setExpiryTimestamp(expiry - now); | ||
} | ||
|
||
setLoading(false); | ||
}; | ||
|
||
getExpiry(); | ||
}, [ | ||
parachain, | ||
coretimeApi, | ||
isCoretimeReady, | ||
relayApi, | ||
isRelayReady, | ||
timeslicePeriod, | ||
saleInfoStatus, | ||
saleStatus, | ||
phase, | ||
]); | ||
|
||
useEffect(() => { | ||
// if expiry is before the next region begin it should be possible to renew. | ||
setRenewalEnabled(parachain.when <= saleInfo.regionBegin); | ||
}, [saleInfo.regionBegin, parachain.when]); | ||
|
||
return ( | ||
<> | ||
<Stack direction='column' gap='1.5rem' margin='1rem 0' width='75%' sx={{ mx: 'auto' }}> | ||
<ParachainInfo | ||
parachain={parachain} | ||
expiryTimestamp={expiryTimestamp} | ||
expiryLoading={loading} | ||
/> | ||
{parachain.when > saleInfo.regionBegin ? ( | ||
<Banner content={'No need to renew in the current sale'} severity='success' /> | ||
) : ( | ||
<> | ||
{/* If all cores are sold warn the user: */} | ||
{saleInfo.coresSold === saleInfo.coresOffered && ( | ||
<Banner | ||
content={ | ||
'No more cores are on sale! Attempting to renew will fail. To avoid these kind of \ | ||
issues in the future, please renew during the interlude phase. ' | ||
} | ||
link={{ | ||
title: 'Renewal FAQ', | ||
href: 'https://docs.regionx.tech/docs/faq/renewal-questions', | ||
}} | ||
severity='warning' | ||
/> | ||
)} | ||
{/* If not all cores are sold inform the user to renew: */} | ||
{saleInfo.coresSold < saleInfo.coresOffered && ( | ||
<Banner | ||
content={ | ||
'It is highly recommended to renew during the interlude phase, as doing so guarantees \ | ||
that the core will be available for renewal. ' | ||
} | ||
link={{ | ||
title: 'Renewal FAQ', | ||
href: 'https://docs.regionx.tech/docs/faq/renewal-questions', | ||
}} | ||
severity='info' | ||
/> | ||
)} | ||
</> | ||
)} | ||
</Stack> | ||
</> | ||
); | ||
}; | ||
|
||
interface ParachainInfoProps { | ||
parachain: RenewableParachain; | ||
expiryTimestamp: number; | ||
expiryLoading: boolean; | ||
} | ||
|
||
const ParachainInfo = ({ parachain, expiryTimestamp, expiryLoading }: ParachainInfoProps) => { | ||
const { | ||
state: { decimals, symbol }, | ||
} = useCoretimeApi(); | ||
|
||
const formatDuration = humanizer({ units: ['w', 'd', 'h'], round: true }); | ||
|
||
return ( | ||
<> | ||
<Stack | ||
direction='column' | ||
padding='1rem' | ||
mt={'1rem'} | ||
gap='0.5rem' | ||
border='1px solid' | ||
borderColor={theme.palette.grey[400]} | ||
borderRadius='1rem' | ||
> | ||
<Property property='Core number:' value={parachain.core.toString()} /> | ||
<Property | ||
tooltip='The parachain will stop with block production once it expires.' | ||
property='Expiry in:' | ||
value={expiryLoading ? '...' : formatDuration(expiryTimestamp)} | ||
/> | ||
<Property | ||
property='Renewal price: ' | ||
value={getBalanceString(parachain.price.toString(), decimals, symbol)} | ||
/> | ||
</Stack> | ||
</> | ||
); | ||
}; | ||
|
||
interface PropertyProps { | ||
property: string; | ||
value: string; | ||
tooltip?: string; | ||
} | ||
|
||
const Property = ({ property, value, tooltip }: PropertyProps) => { | ||
return ( | ||
<Box display='flex' justifyContent='space-between' mx='1rem'> | ||
<Typography fontWeight='light' color='black'> | ||
{property} | ||
</Typography> | ||
<Box display='flex'> | ||
{tooltip && ( | ||
<Tooltip title={tooltip} arrow sx={{ fontSize: '1rem' }}> | ||
<Typography color='black' mr='.5rem' sx={{ cursor: 'default' }}> | ||
ⓘ | ||
</Typography> | ||
</Tooltip> | ||
)} | ||
<Typography fontWeight='bold' color='black'> | ||
{value} | ||
</Typography> | ||
</Box> | ||
</Box> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { Backdrop, Box, CircularProgress, Paper, Typography, useTheme } from '@mui/material'; | ||
import { useState } from 'react'; | ||
|
||
import { useRenewableParachains } from '@/hooks/renewableParas'; | ||
|
||
import { Balance } from '@/components'; | ||
|
||
import { ContextStatus } from '@/models'; | ||
|
||
import { RenewAction } from './action'; | ||
import { RenewableParaInfo } from './info'; | ||
import { SelectParachain } from './select'; | ||
|
||
const Renewal = () => { | ||
const theme = useTheme(); | ||
|
||
const [activeIdx, setActiveIdx] = useState<number>(0); | ||
const [renewalEnabled, setRenewalEnabled] = useState<boolean>(true); | ||
const { status, parachains } = useRenewableParachains(); | ||
|
||
return status !== ContextStatus.LOADED ? ( | ||
<Backdrop open> | ||
<CircularProgress /> | ||
</Backdrop> | ||
) : parachains.length === 0 ? ( | ||
<Typography>There are no renewable parachains.</Typography> | ||
) : ( | ||
<> | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
alignItems: 'center', | ||
}} | ||
> | ||
<Box> | ||
<Typography variant='subtitle1' sx={{ color: theme.palette.common.black }}> | ||
Renew a parachain | ||
</Typography> | ||
<Typography variant='subtitle2' sx={{ color: theme.palette.text.primary }}> | ||
Renew a parachain | ||
</Typography> | ||
</Box> | ||
<Balance ctBalance /> | ||
</Box> | ||
|
||
<Box sx={{ width: '60%', margin: '0 auto' }}> | ||
<Paper | ||
sx={{ | ||
padding: '2rem', | ||
borderRadius: '2rem', | ||
mt: '2rem', | ||
boxShadow: 'none', | ||
}} | ||
> | ||
<SelectParachain | ||
activeIdx={activeIdx} | ||
parachains={parachains} | ||
setActiveIdx={setActiveIdx} | ||
/> | ||
<RenewableParaInfo | ||
parachain={parachains[activeIdx]} | ||
setRenewalEnabled={setRenewalEnabled} | ||
/> | ||
<RenewAction parachain={parachains[activeIdx]} enabled={renewalEnabled} /> | ||
</Paper> | ||
</Box> | ||
</> | ||
); | ||
}; | ||
|
||
export default Renewal; |
Oops, something went wrong.