Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contract detail design + header funtionality #264

Merged
merged 42 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8f7e364
Adding contracts route
henrypalacios Sep 28, 2023
f013ae6
Add Custom Contracts and Contracts section (#195)
alongoni Sep 28, 2023
5201e21
Merge branch 'develop' into epic-custom-contracts
henrypalacios Oct 5, 2023
27607ff
Merge branch 'develop' into epic-custom-contracts
henrypalacios Oct 5, 2023
9f48dfa
Add Contract Table / Share Modal (#217)
bogos Oct 6, 2023
c68e35f
Merge branch 'develop' into epic-custom-contracts
henrypalacios Oct 9, 2023
f270fba
Merge branch 'develop' into epic-custom-contracts
henrypalacios Oct 10, 2023
7568542
Add last contracts (#229)
alongoni Oct 11, 2023
5683f4b
Merge branch 'develop' into epic-custom-contracts
alongoni Oct 13, 2023
0d305e4
fix missing type error
alongoni Oct 13, 2023
7a71c11
Add import form section (#201)
alongoni Oct 18, 2023
f912739
Feature/ Contrat table edit name and delete contract (#232)
bogos Oct 19, 2023
1751e3b
Fix infinite contract table loop (#244)
bogos Oct 23, 2023
ec9dd54
Contracts UI + Layout fixes (#238)
alongoni Oct 25, 2023
8979c19
Contract details view layout (#231)
alongoni Oct 25, 2023
06c54b2
add disabled styles
alongoni Oct 25, 2023
1f7155b
Merge branch 'epic-custom-contracts' into form-elements-styles
alongoni Oct 25, 2023
d7fdbb6
Revert "Contract details view layout (#231)"
henrypalacios Oct 25, 2023
df28515
Fix minnor bugs in contracts page table (#246)
bogos Oct 26, 2023
c69e76a
import custom deployment (#245)
henrypalacios Oct 26, 2023
36af99e
fix minnor custom contracts bugs
bogos Oct 27, 2023
6af0ef7
Merge branch 'epic-custom-contracts' of github.com:protofire/polkadot…
bogos Oct 27, 2023
461f4da
fix contract table rerender bug
bogos Oct 27, 2023
8ebefe3
Fix minnor bugs (#255)
bogos Oct 31, 2023
9b139ca
Form styles and layout fixes (#247)
alongoni Oct 31, 2023
fd430e5
Fix yarn .lock
henrypalacios Oct 31, 2023
9a2e2fa
load custom contract (#248)
henrypalacios Nov 1, 2023
621c25a
Tables UI Improvements + New Documentation (#256)
alongoni Nov 3, 2023
fed68b1
connection validations (#257)
henrypalacios Nov 3, 2023
4378c27
contract detail design + share functionality
alongoni Nov 7, 2023
326ebcc
fix imports
alongoni Nov 7, 2023
f6166f8
move contract-details to view
alongoni Nov 7, 2023
9afba23
add edit contract name and download logic
bogos Nov 9, 2023
0869a56
fix build problem
bogos Nov 9, 2023
bdbd46d
fix deployed by address and edit button container
alongoni Nov 9, 2023
ead381f
Merge branch 'develop' into contract-details-design
henrypalacios Nov 9, 2023
80fd386
update contractDetail
alongoni Nov 9, 2023
c810af2
fix edit name bug
bogos Nov 9, 2023
cc3268d
fix tab padding
alongoni Nov 10, 2023
cd6e761
add action container
alongoni Nov 10, 2023
75b1da2
fix table head aligmnet
alongoni Nov 10, 2023
9ccb2ec
decrease tabs size
alongoni Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/pages/contract-detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { useModalBehaviour } from '@/hooks/useModalBehaviour'
import { useRouter } from 'next/router'
import { useFindUserContract } from '@/hooks/userContracts/useFindUserContract'
import { useHasMounted } from '@/hooks/useHasMounted'
import { useDownloadMetadata } from '@/view/components/ContractsTable/useDownloadMetadata'

export default function CustomContractDetailPage() {
const router = useRouter()
const { uuid } = router.query
const { userContract, requested, isLoading } = useFindUserContract(
uuid as string
)
const { onDownloadSource } = useDownloadMetadata(userContract)
const modalBehaviour = useModalBehaviour()
const hasMounted = useHasMounted()

Expand All @@ -29,6 +31,7 @@ export default function CustomContractDetailPage() {
{userContract && (
<ContractDetail
modalBehaviour={modalBehaviour}
onDownloadSource={onDownloadSource}
userContract={userContract}
/>
)}
Expand Down
1 change: 1 addition & 0 deletions src/services/substrate/MetadataManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class MetadataManager {
* apiPromise
* );
*/

deriveFromJson(
options: DeriveOptions,
source?: Record<string, unknown>,
Expand Down
6 changes: 6 additions & 0 deletions src/utils/inputValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ export function maxAllowed(value: unknown, max = 64): string | void {
if (typeof _value !== 'number' || isNaN(_value) || _value > max)
return `The number can not be greater than ${max}`
}

export function maxLength(value: unknown, max = 20): string | void {
if (typeof value !== 'string' || value.length > max) {
return `The field only accepts 20 letters`
}
}
264 changes: 204 additions & 60 deletions src/view/ContractDetailView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,179 @@
import React from 'react'
import { CopyToClipboardButton } from '@/view/components/CopyButton'
import { MonoTypography } from '@/view/components/MonoTypography'
import { Box, Typography, Stack, Tooltip } from '@mui/material'
import { Box, Typography, Stack, Tooltip, Button } from '@mui/material'
import { DefaultToolTipButton } from '@/view/components/DefaultTooltipButton'
import EditIcon from '@mui/icons-material/Edit'
import ShareIcon from '@mui/icons-material/Share'
import DownloadIcon from '@mui/icons-material/Download'
import { getChain } from '@/constants/chains'
import NetworkBadge from '@/view/components/NetworkBadge'
import { UseModalBehaviour } from '@/hooks/useModalBehaviour'
import { UserContractDetails, UserContractDetailsWithAbi } from '@/domain'
import {
UserContractDetails,
UserContractDetailsWithAbi,
isAbiSource
} from '@/domain'
import { isoDate, isoToReadableDate } from '@/utils/formatString'
isoDate,
isoToReadableDate,
takeLastChars,
truncateAddress
} from '@/utils/formatString'
import { ShareContractModal } from '@/view/components/ShareContractModal'
import { getUserContractUrl } from '@/view/components/ContractsTable/getUserContractUrl'
import { useNetworkAccountsContext } from '@/context/NetworkAccountsContext'
import { ContractsTabInteraction } from '@/view/ContractDetailView/ContractsTabInteraction'
import { ConnectWalletSection } from '@/view/components/ConnectWalletSection'

import { useFormInput } from '@/hooks'
import { maxLength, notEmpty } from '@/utils/inputValidation'
import { StyledTextField } from '../components'
import CheckIcon from '@mui/icons-material/CheckCircleOutlineRounded'
import InfoOutlined from '@mui/icons-material/InfoOutlined'
import CancelIcon from '@mui/icons-material/Cancel'

import { UpdateDeployment } from '@/domain/repositories/DeploymentRepository'
import { useUpdateUserContracts } from '@/hooks/userContracts/useUpdateUserContracts'
import { UserContractTableItem } from '@/domain/wizard/ContractTableItem'
interface Props {
modalBehaviour: UseModalBehaviour
userContract: UserContractDetails
onDownloadSource: (contract: UserContractTableItem) => void
}
interface AbiSource {
source: { language: string }
}

export default function ContractDetail({
modalBehaviour,
userContract
}: Props) {
userContract,
onDownloadSource
}: Props): JSX.Element {
const [openShareModal, setOpenShareModal] = React.useState(false)
const url = getUserContractUrl(userContract)
const { accountConnected } = useNetworkAccountsContext()
if (!userContract) {
return null
const { updateContract } = useUpdateUserContracts()
const [isNameEditable, setIsNameEditable] = React.useState(false)
const chainDetails = getChain(userContract.network)
const abi = userContract.abi as AbiSource | undefined
const formData = {
contractName: useFormInput<string>(userContract.name, [notEmpty, maxLength])
}
const { abi } = userContract
const anyInvalidField: boolean = Object.values(formData).some(
field => (field.required && !field.value) || field.error !== null
)

if (!isAbiSource(abi)) {
return null
const handleUpdateContractName = () => {
const updatedContract: UpdateDeployment = {
address: userContract.address,
userAddress: userContract.userAddress,
network: userContract.network,
name: formData.contractName.value,
hidden: false
}
formData.contractName.setValue(updatedContract.name as string)
updateContract({
deployment: updatedContract
})
setIsNameEditable(!isNameEditable)
}

const chainDetails = getChain(userContract.network)
const stopPropagation = (
event: React.MouseEvent<HTMLButtonElement | HTMLDivElement, MouseEvent>,
onFn: () => void
) => {
event.stopPropagation()
onFn()
}

return (
<>
<Stack direction="row" justifyContent="space-between">
<Stack direction="row" justifyContent="space-between">
<Typography variant="h2">{userContract.name}</Typography>
<DefaultToolTipButton
id="edit-contract-address"
sx={{ marginLeft: '0.5rem', color: 'white' }}
title="Edit"
Icon={EditIcon}
></DefaultToolTipButton>
<DefaultToolTipButton
id="download-contract-address"
sx={{ marginLeft: '0.5rem', color: 'white' }}
title="Download metadata"
Icon={DownloadIcon}
></DefaultToolTipButton>
<DefaultToolTipButton
id="share-contract-address"
sx={{ marginLeft: '0.5rem', color: 'white' }}
title="Share"
Icon={ShareIcon}
onClick={() => modalBehaviour.openModal()}
></DefaultToolTipButton>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
>
{isNameEditable ? (
<>
<StyledTextField
label="Contract Name"
placeholder={userContract.name}
value={formData.contractName.value}
onChange={formData.contractName.onChange}
error={Boolean(formData.contractName.error)}
helperText={
formData.contractName.error ? formData.contractName.error : ''
}
loading={formData.contractName.loading}
autoFocus
/>
<DefaultToolTipButton
id="edit-contract-address"
sx={{
marginLeft: '0.5rem',
color: 'green'
}}
title="Save"
Icon={CheckIcon}
onClick={handleUpdateContractName}
disabled={anyInvalidField}
></DefaultToolTipButton>
<DefaultToolTipButton
id={`cancel-contract-name${takeLastChars(userContract.uuid)}`}
sx={{ color: 'tomato' }}
title="Cancel"
Icon={CancelIcon}
onClick={event =>
stopPropagation(event, () => {
formData.contractName.setValue(userContract.name)
setIsNameEditable(!isNameEditable)
})
}
></DefaultToolTipButton>
</>
) : (
<>
<Typography variant="h2">
{formData.contractName.value}
</Typography>
<DefaultToolTipButton
id="edit-contract-address"
sx={{ marginLeft: '0.5rem', color: 'white' }}
title="Edit"
Icon={EditIcon}
onClick={event =>
stopPropagation(event, () => setIsNameEditable(true))
}
></DefaultToolTipButton>
</>
)}
</Stack>
<Typography variant="body1">
Added {''}
<Tooltip placement="top" title={isoDate(userContract.date)}>
<Typography variant="body1" component="span">
{isoToReadableDate(userContract.date)}
</Typography>
</Tooltip>
</Typography>
<Box display="flex" gap="1rem">
<Button
variant="contained"
endIcon={<DownloadIcon />}
sx={{
borderRadius: '3rem',
maxHeight: '3rem',
backgroundColor: '#20222D'
}}
onClick={event =>
stopPropagation(event, () => onDownloadSource(userContract))
}
>
Download
</Button>
<Button
variant="contained"
endIcon={<ShareIcon />}
color="primary"
onClick={event =>
stopPropagation(event, () => setOpenShareModal(true))
}
sx={{
borderRadius: '3rem',
maxHeight: '3rem'
}}
>
Share
</Button>
</Box>
</Stack>
<Stack direction="row">
<MonoTypography>{userContract.address}</MonoTypography>
Expand All @@ -89,42 +190,80 @@ export default function ContractDetail({
sx={{ margin: '2em 0 3rem' }}
>
<Box display="flex" flexDirection="column">
<Typography variant="caption" align="left">
TYPE
</Typography>
<Box display="flex" flexDirection="row" gap="0.5rem">
<Typography variant="caption" align="left">
TYPE
</Typography>
<Tooltip
placement="top"
title="Type or category of the smart contract. You can find the categories in the 'Builder' page."
>
<InfoOutlined style={{ fontSize: 16 }} />
</Tooltip>
</Box>
<Typography variant="h5" align="left">
{userContract.type}
</Typography>
</Box>
<Box display="flex" flexDirection="column">
<Typography variant="caption" align="left">
NETWORK
</Typography>
<Box display="flex" flexDirection="row" gap="0.5rem">
<Typography variant="caption" align="left">
NETWORK
</Typography>
<Tooltip
placement="top"
title="This network is the one that the contract has been deployed."
>
<InfoOutlined style={{ fontSize: 16 }} />
</Tooltip>
</Box>
<Typography variant="h5" align="left">
<NetworkBadge
name={chainDetails.name}
logo={chainDetails.logo.src}
logoSize={{ width: 20, height: 20 }}
description={chainDetails.logo.alt}
textTooltip="This network is the one that the contract has been deployed."
showTooltip={false}
/>
</Typography>
</Box>
<Box display="flex" flexDirection="column">
<Typography variant="caption" align="left">
LANGUAGE
</Typography>
<Box display="flex" flexDirection="row" gap="0.5rem">
<Typography variant="caption" align="left">
LANGUAGE{' '}
</Typography>
<Tooltip
placement="top"
title="Programming language or technology used to write the smart contract's code."
>
<InfoOutlined style={{ fontSize: 16 }} />
</Tooltip>
</Box>
<Typography variant="h5" align="left">
{abi?.source.language}
</Typography>
</Box>
<Box display="flex" flexDirection="column">
<Typography variant="caption" align="left">
Actions
</Typography>
<Typography variant="h5" align="left">
--
<Typography variant="caption">
Added {''}
<Tooltip placement="top" title={isoDate(userContract.date)}>
<Typography variant="caption" component="span">
{isoToReadableDate(userContract.date)}
</Typography>
</Tooltip>
</Typography>
<Stack direction="row" alignItems="center">
<Typography variant="caption">Deployed by</Typography>
{''}
<MonoTypography sx={{ fontSize: '0.8rem' }}>
{truncateAddress(userContract.userAddress, 4)}
</MonoTypography>
<CopyToClipboardButton
id="copy-contract-address"
sx={{ marginLeft: '0.5rem' }}
data={userContract.address}
/>
</Stack>
</Box>
</Box>
{accountConnected ? (
Expand All @@ -136,6 +275,11 @@ export default function ContractDetail({
text={'You need to connect a wallet to interact with this contract.'}
/>
)}
<ShareContractModal
open={openShareModal}
handleClose={() => setOpenShareModal(false)}
url={url}
></ShareContractModal>
</>
)
}
Loading
Loading