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
|
||||
sudo: required
|
||||
language: node_js
|
||||
node_js: node
|
||||
|
||||
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
|
||||
|
||||
script:
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import '../styles/_variables.css';
|
||||
|
||||
.add {
|
||||
max-width: 40rem;
|
||||
width: 100%;
|
||||
@ -5,3 +7,9 @@
|
||||
word-wrap: break-word;
|
||||
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 { saveToIpfs } from '../ipfs'
|
||||
import { ipfsGateway } from '../../site.config'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { ipfsNodeUri, ipfsGateway } from '../../site.config'
|
||||
import Dropzone from './Dropzone'
|
||||
import styles from './Add.module.css'
|
||||
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() {
|
||||
const { isIpfsReady, ipfsError } = useIpfsApi(ipfsConfig)
|
||||
const [fileHash, setFileHash] = useState()
|
||||
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)
|
||||
const cid = await saveToIpfs(files)
|
||||
setError(null)
|
||||
|
||||
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 (
|
||||
<div className={styles.add}>
|
||||
{loading ? (
|
||||
<Spinner />
|
||||
<Spinner message={message} />
|
||||
) : fileHash ? (
|
||||
<a
|
||||
target="_blank"
|
||||
@ -29,7 +89,16 @@ export default function Add() {
|
||||
ipfs://{fileHash}
|
||||
</a>
|
||||
) : (
|
||||
<Dropzone multiple={false} handleOnDrop={handleCaptureFile} />
|
||||
<>
|
||||
<Dropzone
|
||||
multiple={false}
|
||||
handleOnDrop={handleOnDrop}
|
||||
disabled={!isIpfsReady}
|
||||
/>
|
||||
{(error || ipfsError) && (
|
||||
<div className={styles.error}>{error || ipfsError}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
.disabled {
|
||||
composes: dropzone;
|
||||
opacity: 0.5;
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,17 @@
|
||||
import React from 'react'
|
||||
import styles from './Spinner.module.css'
|
||||
|
||||
export default function Spinner() {
|
||||
return <div className={styles.spinner} />
|
||||
const Spinner = ({ message }: { message?: string }) => {
|
||||
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