diff --git a/package.json b/package.json index aa590430..89682040 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "@polkadot/extension-inject": "latest", "@polkadot/types": "latest", "@polkadot/ui-keyring": "latest", - "@polkadot/util": "latest", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", "@types/humanize-duration": "^3.27.3", "@vercel/analytics": "^1.2.2", "clsx": "^1.1.1", @@ -67,4 +68,4 @@ "typescript": "^4.7.4", "webpack": "^5.81.0" } -} \ No newline at end of file +} diff --git a/src/components/Elements/ListingCard/index.tsx b/src/components/Elements/ListingCard/index.tsx index 029f5276..3f23f2ba 100644 --- a/src/components/Elements/ListingCard/index.tsx +++ b/src/components/Elements/ListingCard/index.tsx @@ -196,7 +196,10 @@ const ListingCardInner = ({ > Price/timeslice: - {`${formatBalance(listing.timeslicePrice.toString(), true)} ${symbol}`} + {`${formatBalance( + listing.timeslicePrice.toString(), + true + )} ${symbol}`} Total: - {`${formatBalance(listing.currentPrice.toString(), true)} ${symbol}`} + {`${formatBalance( + listing.currentPrice.toString(), + true + )} ${symbol}`} diff --git a/src/components/Elements/RegionCard/index.tsx b/src/components/Elements/RegionCard/index.tsx index 55381e5f..b34f69bc 100644 --- a/src/components/Elements/RegionCard/index.tsx +++ b/src/components/Elements/RegionCard/index.tsx @@ -26,7 +26,7 @@ import { useRelayApi } from '@/contexts/apis'; import { ApiState } from '@/contexts/apis/types'; import { useCommon } from '@/contexts/common'; import { useTasks } from '@/contexts/tasks'; -import { RegionLocation, RegionMetadata } from '@/models'; +import { POOLING_TASK_ID, RegionLocation, RegionMetadata } from '@/models'; import styles from './index.module.scss'; import { Label } from '..'; @@ -168,6 +168,7 @@ const RegionCardInner = ({ }; const getTask = (taskId: number | null): string => { + if (taskId === POOLING_TASK_ID) return 'Instantaneous Pool'; const getTaskName = (taskId: number) => { return tasks.find(({ id }) => id === taskId)?.name || ''; }; diff --git a/src/components/Elements/Selectors/FinalitySelector/index.module.scss b/src/components/Elements/Selectors/FinalitySelector/index.module.scss new file mode 100644 index 00000000..c994ef44 --- /dev/null +++ b/src/components/Elements/Selectors/FinalitySelector/index.module.scss @@ -0,0 +1,26 @@ +.options { + display: flex; + justify-content: center; +} + +.option, +.activeOption { + min-width: 15rem; + margin: 1rem 5rem; + border-radius: 0.5rem !important; + text-transform: capitalize; +} + +.activeOption { + border: 1px solid #e84d68; + background: linear-gradient(180deg, #e84d68 0%, #ad2b49 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.option { + border: 1px solid #cccccc !important; + background-color: white; + color: black; +} diff --git a/src/components/Elements/Selectors/FinalitySelector/index.tsx b/src/components/Elements/Selectors/FinalitySelector/index.tsx new file mode 100644 index 00000000..2b974e8e --- /dev/null +++ b/src/components/Elements/Selectors/FinalitySelector/index.tsx @@ -0,0 +1,27 @@ +import { MenuItem, Select } from '@mui/material'; + +import { FinalityType } from '@/models'; + +interface FinalitySelectorProps { + finality: FinalityType; + setFinality: (_: FinalityType) => void; +} + +export const FinalitySelector = ({ + finality, + setFinality, +}: FinalitySelectorProps) => { + return ( + + ); +}; diff --git a/src/components/Elements/Selectors/index.ts b/src/components/Elements/Selectors/index.ts index 3246a937..286b62e4 100644 --- a/src/components/Elements/Selectors/index.ts +++ b/src/components/Elements/Selectors/index.ts @@ -1,4 +1,5 @@ export * from './AssetSelector'; export * from './ChainSelector'; +export * from './FinalitySelector'; export * from './RecipientSelector'; export * from './RegionSelector'; diff --git a/src/components/Modals/Pooling/index.module.scss b/src/components/Modals/Pooling/index.module.scss new file mode 100644 index 00000000..779e8db1 --- /dev/null +++ b/src/components/Modals/Pooling/index.module.scss @@ -0,0 +1,26 @@ +.container { + display: flex; + flex-direction: column; + gap: 0.75rem; + min-width: 35rem; +} + +.content { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin: 1rem 0; +} + +.options { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; +} + +.optionItem { + display: flex; + flex-direction: column; + gap: 0.5rem; +} diff --git a/src/components/Modals/Pooling/index.tsx b/src/components/Modals/Pooling/index.tsx new file mode 100644 index 00000000..4a48c077 --- /dev/null +++ b/src/components/Modals/Pooling/index.tsx @@ -0,0 +1,173 @@ +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + Input, + InputAdornment, + Paper, + Typography, + useTheme, +} from '@mui/material'; +import { useEffect, useState } from 'react'; + +import { isValidAddress } from '@/utils/functions'; + +import { + FinalitySelector, + ProgressButton, + SimpleRegionCard, +} from '@/components/Elements'; + +import { useAccounts } from '@/contexts/account'; +import { useCoretimeApi } from '@/contexts/apis'; +import { useRegions } from '@/contexts/regions'; +import { useToast } from '@/contexts/toast'; +import { FinalityType, RegionMetadata } from '@/models'; + +import styles from './index.module.scss'; + +interface PoolingModalProps { + open: boolean; + onClose: () => void; + regionMetadata: RegionMetadata; +} + +export const PoolingModal = ({ + open, + onClose, + regionMetadata, +}: PoolingModalProps) => { + const theme = useTheme(); + const { + state: { activeAccount, activeSigner }, + } = useAccounts(); + + const { + state: { api: coretimeApi }, + } = useCoretimeApi(); + const { fetchRegions } = useRegions(); + const { toastError, toastInfo, toastSuccess } = useToast(); + + const [finality, setFinality] = useState(FinalityType.FINAL); + const [payee, setPayee] = useState(''); + + const [working, setWorking] = useState(false); + + const onPool = async () => { + if (!coretimeApi || !activeAccount || !activeSigner) return; + + const txPooling = coretimeApi.tx.broker.pool( + regionMetadata.region.getOnChainRegionId(), // region id + activeAccount.address, // payee + finality + ); + + try { + setWorking(true); + await txPooling.signAndSend( + activeAccount.address, + { signer: activeSigner }, + ({ status, events }) => { + if (status.isReady) toastInfo('Transaction was initiated'); + else if (status.isInBlock) toastInfo(`In Block`); + else if (status.isFinalized) { + setWorking(false); + events.forEach(({ event: { method } }) => { + if (method === 'ExtrinsicSuccess') { + toastSuccess( + 'Successfully contributed to the instantaneous region pool' + ); + onClose(); + fetchRegions(); + } else if (method === 'ExtrinsicFailed') { + toastError( + `Failed to contribute to the instantaneous region pool` + ); + } + }); + } + } + ); + } catch (e) { + toastError(`Failed to contribute to the instantaneous region pool ${e}`); + setWorking(false); + } + }; + + useEffect(() => { + if (!open) return; + setFinality(FinalityType.FINAL); + setPayee(''); + setWorking(false); + }, [open]); + + return ( + + + + + Task Pooling + + + You can contribute your region to the pool of instantaneous regions + + + + + + + + Contribution options + + + Finality: + + + + Payee: + setPayee(e.target.value)} + fullWidth + type='text' + placeholder='Address of the payee' + endAdornment={ + + + + } + sx={{ height: '3rem' }} + error={payee.length > 0 && !isValidAddress(payee)} + /> + + + + + + + + + + ); +}; diff --git a/src/components/Modals/TaskAssign/index.module.scss b/src/components/Modals/TaskAssign/index.module.scss index cd583cf3..342d1d4f 100644 --- a/src/components/Modals/TaskAssign/index.module.scss +++ b/src/components/Modals/TaskAssign/index.module.scss @@ -18,3 +18,9 @@ padding: 1.25rem; gap: 1rem; } + +.alert { + max-width: 32rem; + align-items: center; + margin: 0 auto; +} diff --git a/src/components/Modals/TaskAssign/index.tsx b/src/components/Modals/TaskAssign/index.tsx index ed9dfc44..47c6129d 100644 --- a/src/components/Modals/TaskAssign/index.tsx +++ b/src/components/Modals/TaskAssign/index.tsx @@ -165,14 +165,12 @@ export const TaskAssignModal = ({ + + Finally assigned regions can no longer be managed.
+ They will not be displayed on the Region Management page anymore. +
- - Finally assigned regions can no longer be managed. They will not be - displayed on the Region Management page anymore. - +