diff --git a/.travis.yml b/.travis.yml
index 772754b..d33b9b1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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:
diff --git a/src/components/Add.module.css b/src/components/Add.module.css
index e8943aa..203bd0b 100644
--- a/src/components/Add.module.css
+++ b/src/components/Add.module.css
@@ -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);
+}
diff --git a/src/components/Add.tsx b/src/components/Add.tsx
index b982048..36f01a4 100644
--- a/src/components/Add.tsx
+++ b/src/components/Add.tsx
@@ -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
+ ${fileSizeReceived || 0}/${fileSize}
`
+ )
+ }, [fileSize, fileSizeReceived])
+
+ async function handleOnDrop(acceptedFiles: File[]) {
+ if (!acceptedFiles[0]) return
- const handleCaptureFile = async (files: File[]) => {
setLoading(true)
- const cid = await saveToIpfs(files)
- setFileHash(cid)
- setLoading(false)
+ 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 (