Skip to content

Commit

Permalink
Renewal improvements (#232)
Browse files Browse the repository at this point in the history
* renewal refactor

* lint & format

* info cleanup

* format & improvements

* improvements & cleanup

* move to components

* fix

* format core
  • Loading branch information
Szegoo authored Aug 31, 2024
1 parent c6e8eb0 commit ab8b692
Show file tree
Hide file tree
Showing 11 changed files with 393 additions and 300 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The UI currently supports:
- Parachain Id reservation
- Parachain code registration

### `/paras/renewal`
### `/renew`
- Core renewal

### `/marketplace`
Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout/Sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const Sidebar = () => {
parachains: [
{
label: 'Renew',
route: '/paras/renewal',
route: '/renew',
enabled: true,
icon: <RenewIcon color={theme.palette.text.primary} />,
},
Expand Down
6 changes: 5 additions & 1 deletion src/components/Paras/ParaDisplay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import Unknown from '../../../assets/unknown.svg';
interface ParaDisplayProps {
paraId: number;
network: NetworkType;
core?: number;
}
export const ParaDisplay = ({ paraId, network }: ParaDisplayProps) => {

export const ParaDisplay = ({ paraId, network, core }: ParaDisplayProps) => {
const data = chainData[network][paraId];

if (data === undefined)
return (
<Stack direction='row' alignItems='center' gap='0.5rem'>
<Image src={Unknown} width={32} height={32} style={{ borderRadius: '100%' }} alt='' />
Parachain #{paraId}
{core && <p>| Core {core}</p>}
</Stack>
);

Expand All @@ -32,6 +35,7 @@ export const ParaDisplay = ({ paraId, network }: ParaDisplayProps) => {
)}
{name}
<p>#{paraId}</p>
{core && <p>| Core {core}</p>}
</Stack>
);
};
70 changes: 70 additions & 0 deletions src/components/Renew/action.tsx
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>
</>
);
};
193 changes: 193 additions & 0 deletions src/components/Renew/info.tsx
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' }}>
&#9432;
</Typography>
</Tooltip>
)}
<Typography fontWeight='bold' color='black'>
{value}
</Typography>
</Box>
</Box>
);
};
72 changes: 72 additions & 0 deletions src/components/Renew/renewPage.tsx
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;
Loading

0 comments on commit ab8b692

Please sign in to comment.