Skip to content

Commit

Permalink
working on drag and drop logic
Browse files Browse the repository at this point in the history
  • Loading branch information
alongoni committed Oct 2, 2023
1 parent 5d2e66d commit c6ef24d
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 12 deletions.
5 changes: 5 additions & 0 deletions src/domain/FileState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface FileState {
data: Uint8Array
name: string
size: number
}
19 changes: 19 additions & 0 deletions src/domain/Metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Abi } from '@/services/substrate/types'

export interface Validation {
isError?: boolean
isSuccess?: boolean
isTouched?: boolean
isValid?: boolean
isWarning?: boolean
message?: React.ReactNode
}

export interface Metadata {
source?: Record<string, unknown>
name: string
value?: Abi
isSupplied: boolean
}

export type MetadataState = Metadata & Validation
1 change: 1 addition & 0 deletions src/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './UserContractDetails'
export * from './DomainEvents'
export * from './TokenType'
export * from './WizardContractConfig'
export * from './Metadata'
76 changes: 76 additions & 0 deletions src/hooks/useParseMetadataField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState
} from 'react'
import { useApi } from 'useink'

import { MetadataState } from '@/domain'
import { MetadataManager } from '@/services/substrate/MetadataManager'
import { readerAsFileState } from '@/utils/fileReader'

type OnChange = Dispatch<SetStateAction<File | null>>
type OnRemove = () => void

interface Options {
isWasmRequired?: boolean
}

interface Callbacks {
onChange?: OnChange
onRemove?: OnRemove
}

export interface UseMetadata {
metadata: MetadataState
onRemove: () => void
metadataFile: File | undefined
onChange: (_file: File) => void
}

const metadataManager = new MetadataManager()

export function useParseMetadataField(
initialValue?: Record<string, unknown>,
options: Options & Callbacks = {}
): UseMetadata {
const [metadataFile, setMetadataFile] = useState<File | undefined>()
const apiProvider = useApi()
const apiPromise = useMemo(() => apiProvider?.api, [apiProvider?.api])
const { isWasmRequired = false } = options
const [metadata, setMetadata] = useState<MetadataState>(metadataManager.EMPTY)

const onChange = useCallback(
async (file: File) => {
setMetadataFile(file)
const fileState = await readerAsFileState(file)
const newState = metadataManager.parseFile(
fileState,
isWasmRequired,
apiPromise
)

setMetadata(newState)
},
[apiPromise, isWasmRequired]
)

const onRemove = useCallback(() => {
setMetadataFile(undefined)
setMetadata(metadataManager.EMPTY)
}, [setMetadataFile])

useEffect(() => {
metadataFile && onChange(metadataFile)
}, [metadataFile, onChange])

return {
metadata,
metadataFile,
onChange: setMetadataFile,
onRemove
}
}
18 changes: 6 additions & 12 deletions src/pages/custom/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Box, Stack, Typography } from '@mui/material'
import { StyledTextField } from '@/components'
import { useParseMetadataField } from '@/hooks/useParseMetadataField'
import { DropZone } from '@/view/components/DropZone'
import { DropzoneWrapper } from '@/view/components/DropZone/DropzoneWrapper'

export default function CustomContracts() {
const { metadata, metadataFile, onChange, onRemove } = useParseMetadataField()
return (
<Box
sx={{
Expand All @@ -24,16 +24,10 @@ export default function CustomContracts() {
<DropzoneWrapper>
<DropZone
label="Drop a .json file or click to select it"
accept={{}}
file={undefined}
onChange={function (_file: File): void {
throw new Error('Function not implemented.')
}}
onRemove={function (): void {
throw new Error('Function not implemented.')
}} // file={metadataFile}
// onChange={onChange}
// onRemove={_onRemove}
accept={{ 'application/json': ['.json', '.contract'] }}
file={metadataFile}
onChange={onChange}
onRemove={_onRemove}

Check failure on line 30 in src/pages/custom/index.tsx

View workflow job for this annotation

GitHub Actions / Run tests (18.x)

Cannot find name '_onRemove'. Did you mean 'onRemove'?
/>
</DropzoneWrapper>
</Stack>
Expand Down
121 changes: 121 additions & 0 deletions src/services/substrate/MetadataManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { MetadataState, Validation } from '@/domain'
import { FileState } from '@/domain/FileState'
import { Abi, ApiPromise } from '@/services/substrate/types'

interface Options {
isWasmRequired?: boolean
}

interface DeriveOptions extends Options {
name?: string
}

export class MetadataManager {
public EMPTY: MetadataState = {
isError: false,
isSupplied: false,
isValid: false,
name: '',
message: null
}

private utf8decoder = new TextDecoder()

deriveFromJson(
options: DeriveOptions,
source?: Record<string, unknown>,
api?: ApiPromise | null
): MetadataState {
if (!source) {
return this.EMPTY
}

let value: Abi | undefined = undefined

try {
const apiResult = api?.registry.getChainProperties()
value = new Abi(source, apiResult)

const name = options.name || value.info.contract.name.toString()

return {
source,
name,
value,
isSupplied: true,
...this.validate(value, options)
}
} catch (e) {
console.error(e)

return {
source,
name: '',
value,
isSupplied: true,
...this.validate(value, options)
}
}
}

validate(metadata: Abi | undefined, { isWasmRequired }: Options): Validation {
if (!metadata) {
return {
isValid: false,
isError: true,
message:
'Invalid contract file format. Please upload the generated .contract bundle for your smart contract.'
}
}

const wasm = metadata.info.source.wasm
const isWasmEmpty = wasm.isEmpty
const isWasmInvalid = !WebAssembly.validate(wasm)

if (isWasmRequired && (isWasmEmpty || isWasmInvalid)) {
return {
isValid: false,
isError: true,
message: 'This contract bundle has an empty or invalid WASM field.'
}
}

return {
isValid: true,
isError: false,
isSuccess: true,
message: isWasmRequired
? 'Valid contract bundle!'
: 'Valid metadata file!'
}
}

parseFile(
file: FileState,
isWasmRequired: boolean,
api?: ApiPromise | null
): MetadataState {
try {
const json = JSON.parse(this.utf8decoder.decode(file.data)) as Record<
string,
unknown
>
const name = file.name
.replace('.contract', '')
.replace('.json', '')
.replace('_', ' ')

return this.deriveFromJson({ isWasmRequired, name }, json, api)
} catch (error) {
console.error(error)

return {
...this.EMPTY,
message: 'This contract file is not in a valid format.',
isError: true,
isSupplied: true,
isValid: false
}
}
}
}
Loading

0 comments on commit c6ef24d

Please sign in to comment.