mirror of
https://github.com/oceanprotocol/ipfs
synced 2024-11-21 17:26:59 +01:00
add more feedback
This commit is contained in:
parent
6d377c19cd
commit
f998a90546
@ -1,9 +1,15 @@
|
|||||||
dist: xenial
|
dist: xenial
|
||||||
|
sudo: required
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: node
|
node_js: node
|
||||||
|
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
# Fixes an issue where the max file watch count is exceeded, triggering ENOSPC
|
||||||
|
# https://stackoverflow.com/questions/22475849/node-js-error-enospc#32600959
|
||||||
|
- echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||||
|
|
||||||
# will run `npm install` automatically here
|
# will run `npm install` automatically here
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@import '../styles/_variables.css';
|
||||||
|
|
||||||
.add {
|
.add {
|
||||||
max-width: 40rem;
|
max-width: 40rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -5,3 +7,9 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--red);
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
}
|
||||||
|
@ -1,25 +1,85 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { saveToIpfs } from '../ipfs'
|
import { ipfsNodeUri, ipfsGateway } from '../../site.config'
|
||||||
import { ipfsGateway } from '../../site.config'
|
|
||||||
import Dropzone from './Dropzone'
|
import Dropzone from './Dropzone'
|
||||||
import styles from './Add.module.css'
|
import styles from './Add.module.css'
|
||||||
import Spinner from './Spinner'
|
import Spinner from './Spinner'
|
||||||
|
import useIpfsApi, { IpfsConfig } from '../hooks/use-ipfs-api'
|
||||||
|
|
||||||
|
export function formatBytes(a: number, b: number) {
|
||||||
|
if (a === 0) return '0 Bytes'
|
||||||
|
const c = 1024
|
||||||
|
const d = b || 2
|
||||||
|
const e = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||||
|
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||||
|
return parseFloat((a / Math.pow(c, f)).toFixed(d)) + ' ' + e[f]
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hostname, port, protocol } = new URL(ipfsNodeUri)
|
||||||
|
|
||||||
|
const ipfsConfig: IpfsConfig = {
|
||||||
|
protocol: protocol.replace(':', ''),
|
||||||
|
host: hostname,
|
||||||
|
port: port || '443'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addToIpfs(
|
||||||
|
files: File[],
|
||||||
|
setFileSizeReceived: (size: string) => void
|
||||||
|
) {
|
||||||
|
const { ipfs } = useIpfsApi(ipfsConfig)
|
||||||
|
const file = [...files][0]
|
||||||
|
const fileDetails = { path: file.name, content: file }
|
||||||
|
|
||||||
|
const response = await ipfs.add(fileDetails, {
|
||||||
|
wrapWithDirectory: true,
|
||||||
|
progress: (length: number) => setFileSizeReceived(formatBytes(length, 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
// CID of wrapping directory is returned last
|
||||||
|
const cid = response[response.length - 1].hash
|
||||||
|
return cid
|
||||||
|
}
|
||||||
|
|
||||||
export default function Add() {
|
export default function Add() {
|
||||||
|
const { isIpfsReady, ipfsError } = useIpfsApi(ipfsConfig)
|
||||||
const [fileHash, setFileHash] = useState()
|
const [fileHash, setFileHash] = useState()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [message, setMessage] = useState()
|
||||||
|
const [error, setError] = useState()
|
||||||
|
const [fileSize, setFileSize] = useState()
|
||||||
|
const [fileSizeReceived, setFileSizeReceived] = useState('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMessage(
|
||||||
|
`Adding to IPFS<br />
|
||||||
|
<small>${fileSizeReceived || 0}/${fileSize}</small><br />`
|
||||||
|
)
|
||||||
|
}, [fileSize, fileSizeReceived])
|
||||||
|
|
||||||
|
async function handleOnDrop(acceptedFiles: File[]) {
|
||||||
|
if (!acceptedFiles[0]) return
|
||||||
|
|
||||||
const handleCaptureFile = async (files: File[]) => {
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const cid = await saveToIpfs(files)
|
setError(null)
|
||||||
setFileHash(cid)
|
|
||||||
setLoading(false)
|
const totalSize = formatBytes(acceptedFiles[0].size, 0)
|
||||||
|
setFileSize(totalSize)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cid = await addToIpfs(acceptedFiles, setFileSizeReceived)
|
||||||
|
if (!cid) return
|
||||||
|
setFileHash(cid)
|
||||||
|
setLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
setError(`Adding to IPFS failed: ${error.message}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.add}>
|
<div className={styles.add}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Spinner />
|
<Spinner message={message} />
|
||||||
) : fileHash ? (
|
) : fileHash ? (
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -29,7 +89,16 @@ export default function Add() {
|
|||||||
ipfs://{fileHash}
|
ipfs://{fileHash}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<Dropzone multiple={false} handleOnDrop={handleCaptureFile} />
|
<>
|
||||||
|
<Dropzone
|
||||||
|
multiple={false}
|
||||||
|
handleOnDrop={handleOnDrop}
|
||||||
|
disabled={!isIpfsReady}
|
||||||
|
/>
|
||||||
|
{(error || ipfsError) && (
|
||||||
|
<div className={styles.error}>{error || ipfsError}</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
composes: dropzone;
|
composes: dropzone;
|
||||||
opacity: 0.5;
|
opacity: 0.3;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styles from './Spinner.module.css'
|
import styles from './Spinner.module.css'
|
||||||
|
|
||||||
export default function Spinner() {
|
const Spinner = ({ message }: { message?: string }) => {
|
||||||
return <div className={styles.spinner} />
|
return (
|
||||||
|
<div className={styles.spinner}>
|
||||||
|
{message && (
|
||||||
|
<div
|
||||||
|
className={styles.spinnerMessage}
|
||||||
|
dangerouslySetInnerHTML={{ __html: message }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Spinner
|
||||||
|
49
src/hooks/use-ipfs-api.tsx
Normal file
49
src/hooks/use-ipfs-api.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import ipfsClient from 'ipfs-http-client'
|
||||||
|
|
||||||
|
let ipfs: any = null
|
||||||
|
let ipfsVersion = ''
|
||||||
|
|
||||||
|
export interface IpfsConfig {
|
||||||
|
protocol: string
|
||||||
|
host: string
|
||||||
|
port: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useIpfsApi(config: IpfsConfig) {
|
||||||
|
const [isIpfsReady, setIpfsReady] = useState(Boolean(ipfs))
|
||||||
|
const [ipfsError, setIpfsError] = useState('')
|
||||||
|
|
||||||
|
async function initIpfs() {
|
||||||
|
if (ipfs !== null) return
|
||||||
|
// eslint-disable-next-line
|
||||||
|
ipfs = await ipfsClient(config)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const version = await ipfs.version()
|
||||||
|
ipfsVersion = version.version
|
||||||
|
} catch (error) {
|
||||||
|
setIpfsError(`IPFS connection error: ${error.message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIpfsReady(Boolean(await ipfs.id()))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initIpfs()
|
||||||
|
}, [config])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// just like componentWillUnmount()
|
||||||
|
return function cleanup() {
|
||||||
|
if (ipfs) {
|
||||||
|
setIpfsReady(false)
|
||||||
|
ipfs = null
|
||||||
|
ipfsVersion = ''
|
||||||
|
setIpfsError('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { ipfs, ipfsVersion, isIpfsReady, ipfsError }
|
||||||
|
}
|
35
src/ipfs.ts
35
src/ipfs.ts
@ -1,35 +0,0 @@
|
|||||||
import ipfsClient from 'ipfs-http-client'
|
|
||||||
import { ipfsNodeUri } from '../site.config'
|
|
||||||
|
|
||||||
export async function saveToIpfs(files: File[]) {
|
|
||||||
const { hostname, port, protocol } = new URL(ipfsNodeUri)
|
|
||||||
|
|
||||||
const ipfsConfig = {
|
|
||||||
protocol: protocol.replace(':', ''),
|
|
||||||
host: hostname,
|
|
||||||
port: port || '443'
|
|
||||||
}
|
|
||||||
|
|
||||||
const ipfs = ipfsClient(ipfsConfig)
|
|
||||||
|
|
||||||
const file = [...files][0]
|
|
||||||
let ipfsId
|
|
||||||
const fileDetails = {
|
|
||||||
path: file.name,
|
|
||||||
content: file
|
|
||||||
}
|
|
||||||
const options = {
|
|
||||||
wrapWithDirectory: true,
|
|
||||||
progress: (prog: number) => console.log(`received: ${prog}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await ipfs.add(fileDetails, options)
|
|
||||||
|
|
||||||
// CID of wrapping directory is returned last
|
|
||||||
ipfsId = `${response[response.length - 1].hash}/${fileDetails.path}`
|
|
||||||
return ipfsId
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error.message)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user