Skip to content

Commit

Permalink
Merge pull request #876 from substrate-developer-hub/jc/tut-poe-fix
Browse files Browse the repository at this point in the history
Fix on front-end-tpl code in tut: PoE
  • Loading branch information
Sacha Lansky authored Mar 7, 2022
2 parents dc2aed5 + 7983e89 commit 48e0479
Showing 1 changed file with 138 additions and 141 deletions.
279 changes: 138 additions & 141 deletions v3/tutorials/02-proof-of-existence/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -429,152 +429,149 @@ This React component enables you to expose the proof-of-existence capabilities a
1. Copy and paste the following code into the`src/TemplateModule.js` file:
```javascript
// React and Semantic UI elements.
import React, { useState, useEffect } from 'react'
import { Form, Input, Grid, Message } from 'semantic-ui-react'
// Pre-built Substrate front-end utilities for connecting to a node
// and making a transaction.
import { useSubstrate } from './substrate-lib'
import { TxButton } from './substrate-lib/components'
// Polkadot-JS utilities for hashing data.
import { blake2AsHex } from '@polkadot/util-crypto'
// Main Proof Of Existence component is exported.
export function Main(props) {
// Establish an API to talk to the Substrate node.
const { api } = useSubstrate()
// Get the selected user from the `AccountSelector` component.
const { accountPair } = props
// React hooks for all the state variables we track.
// Learn more at: https://reactjs.org/docs/hooks-intro.html
const [status, setStatus] = useState('')
const [digest, setDigest] = useState('')
const [owner, setOwner] = useState('')
const [block, setBlock] = useState(0)
// Our `FileReader()` which is accessible from our functions below.
let fileReader
// Takes our file, and creates a digest using the Blake2 256 hash function
const bufferToDigest = () => {
// Turns the file content to a hexadecimal representation.
const content = Array.from(new Uint8Array(fileReader.result))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
const hash = blake2AsHex(content, 256)
setDigest(hash)
}
// Callback function for when a new file is selected.
const handleFileChosen = file => {
fileReader = new FileReader()
fileReader.onloadend = bufferToDigest
fileReader.readAsArrayBuffer(file)
}
// React hook to update the owner and block number information for a file
useEffect(() => {
let unsubscribe
// Polkadot-JS API query to the `proofs` storage item in our pallet.
// This is a subscription, so it will always get the latest value,
// even if it changes.
api.query.templateModule
.proofs(digest, result => {
// Our storage item returns a tuple, which is represented as an array.
setOwner(result[0].toString())
setBlock(result[1].toNumber())
})
.then(unsub => {
unsubscribe = unsub
})
return () => unsubscribe && unsubscribe()
// This tells the React hook to update whenever the file digest changes
// (when a new file is chosen), or when the storage subscription says the
// value of the storage item has updated.
}, [digest, api.query.templateModule])
// We can say a file digest is claimed if the stored block number is not 0
function isClaimed() {
return block !== 0
}
// The actual UI elements which are returned from our component.
return (
<Grid.Column>
<h1>Proof of Existence</h1>
{/* Show warning or success message if the file is or is not claimed. */}
<Form success={!!digest && !isClaimed()} warning={isClaimed()}>
<Form.Field>
{/* File selector with a callback to `handleFileChosen`. */}
<Input
type="file"
id="file"
label="Your File"
onChange={e => handleFileChosen(e.target.files[0])}
/>
{/* Show this message if the file is available to be claimed */}
<Message success header="File Digest Unclaimed" content={digest} />
{/* Show this message if the file is already claimed. */}
<Message
warning
header="File Digest Claimed"
list={[digest, `Owner: ${owner}`, `Block: ${block}`]}
/>
</Form.Field>
{/* Buttons for interacting with the component. */}
<Form.Field>
{/* Button to create a claim. Only active if a file is selected, and not already claimed. Updates the `status`. */}
<TxButton
accountPair={accountPair}
label={'Create Claim'}
setStatus={setStatus}
type="SIGNED-TX"
disabled={isClaimed() || !digest}
attrs={{
palletRpc: 'templateModule',
callable: 'createClaim',
inputParams: [digest],
paramFields: [true],
}}
/>
{/* Button to revoke a claim. Only active if a file is selected, and is already claimed. Updates the `status`. */}
<TxButton
accountPair={accountPair}
label="Revoke Claim"
setStatus={setStatus}
type="SIGNED-TX"
disabled={!isClaimed() || owner !== accountPair.address}
attrs={{
palletRpc: 'templateModule',
callable: 'revokeClaim',
inputParams: [digest],
paramFields: [true],
}}
/>
</Form.Field>
{/* Status message about the transaction. */}
<div style={{ overflowWrap: 'break-word' }}>{status}</div>
</Form>
</Grid.Column>
)
}
export default function TemplateModule(props) {
const { api } = useSubstrate()
return api.query.templateModule && api.query.templateModule.proofs ? (
<Main {...props} />
) : null
}
```
```javascript
import React, { useEffect, useState } from 'react'
import { Form, Input, Grid, Message } from 'semantic-ui-react'
// Pre-built Substrate front-end utilities for connecting to a node
// and making a transaction.
import { useSubstrateState } from './substrate-lib'
import { TxButton } from './substrate-lib/components'
// Polkadot-JS utilities for hashing data.
import { blake2AsHex } from '@polkadot/util-crypto'
// Main Proof Of Existence component is exported.
function Main(props) {
// Establish an API to talk to the Substrate node.
const { api, currentAccount } = useSubstrateState()
// React hooks for all the state variables we track.
// Learn more at: https://reactjs.org/docs/hooks-intro.html
const [status, setStatus] = useState('')
const [digest, setDigest] = useState('')
const [owner, setOwner] = useState('')
const [block, setBlock] = useState(0)
// Our `FileReader()` which is accessible from our functions below.
let fileReader
// Takes our file, and creates a digest using the Blake2 256 hash function
const bufferToDigest = () => {
// Turns the file content to a hexadecimal representation.
const content = Array.from(new Uint8Array(fileReader.result))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
const hash = blake2AsHex(content, 256)
setDigest(hash)
}
// Callback function for when a new file is selected.
const handleFileChosen = file => {
fileReader = new FileReader()
fileReader.onloadend = bufferToDigest
fileReader.readAsArrayBuffer(file)
}
// React hook to update the owner and block number information for a file
useEffect(() => {
let unsubscribe
// Polkadot-JS API query to the `proofs` storage item in our pallet.
// This is a subscription, so it will always get the latest value,
// even if it changes.
api.query.templateModule
.proofs(digest, result => {
// Our storage item returns a tuple, which is represented as an array.
setOwner(result[0].toString())
setBlock(result[1].toNumber())
})
.then(unsub => {
unsubscribe = unsub
})
return () => unsubscribe && unsubscribe()
// This tells the React hook to update whenever the file digest changes
// (when a new file is chosen), or when the storage subscription says the
// value of the storage item has updated.
}, [digest, api.query.templateModule])
// We can say a file digest is claimed if the stored block number is not 0
function isClaimed() {
return block !== 0
}
// The actual UI elements which are returned from our component.
return (
<Grid.Column>
<h1>Proof of Existence</h1>
{/* Show warning or success message if the file is or is not claimed. */}
<Form success={!!digest && !isClaimed()} warning={isClaimed()}>
<Form.Field>
{/* File selector with a callback to `handleFileChosen`. */}
<Input
type="file"
id="file"
label="Your File"
onChange={e => handleFileChosen(e.target.files[0])}
/>
{/* Show this message if the file is available to be claimed */}
<Message success header="File Digest Unclaimed" content={digest} />
{/* Show this message if the file is already claimed. */}
<Message
warning
header="File Digest Claimed"
list={[digest, `Owner: ${owner}`, `Block: ${block}`]}
/>
</Form.Field>
{/* Buttons for interacting with the component. */}
<Form.Field>
{/* Button to create a claim. Only active if a file is selected, and not already claimed. Updates the `status`. */}
<TxButton
label="Create Claim"
type="SIGNED-TX"
setStatus={setStatus}
disabled={isClaimed() || !digest}
attrs={{
palletRpc: 'templateModule',
callable: 'createClaim',
inputParams: [digest],
paramFields: [true],
}}
/>
{/* Button to revoke a claim. Only active if a file is selected, and is already claimed. Updates the `status`. */}
<TxButton
label="Revoke Claim"
type="SIGNED-TX"
setStatus={setStatus}
disabled={!isClaimed() || owner !== currentAccount.address}
attrs={{
palletRpc: 'templateModule',
callable: 'revokeClaim',
inputParams: [digest],
paramFields: [true],
}}
/>
</Form.Field>
{/* Status message about the transaction. */}
<div style={{ overflowWrap: 'break-word' }}>{status}</div>
</Form>
</Grid.Column>
)
}
export default function TemplateModule(props) {
const { api } = useSubstrateState()
return api.query.templateModule ? <Main {...props} /> : null
}
```
1. Save your changes and close the file.
1. Start the front-end template by running the following command:
```bash
yarn start
```
```bash
# If you haven't installed the dependencies
yarn install
# Start the project
yarn start
```
This will open up a new tab with the front-end serving at **http://localhost:8000**.
Expand Down

0 comments on commit 48e0479

Please sign in to comment.