From 2104d1bd480245b033f39f143c9017de409b3e25 Mon Sep 17 00:00:00 2001
From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com>
Date: Wed, 7 Feb 2024 13:48:20 -0800
Subject: [PATCH] feat(verified-fetch): improve UX
---
.../package.json | 3 +-
.../src/App.tsx | 196 ++++++++++++++++--
2 files changed, 179 insertions(+), 20 deletions(-)
diff --git a/examples/helia-browser-react-verified-fetch/package.json b/examples/helia-browser-react-verified-fetch/package.json
index bcf1b89c..4d995a25 100644
--- a/examples/helia-browser-react-verified-fetch/package.json
+++ b/examples/helia-browser-react-verified-fetch/package.json
@@ -10,7 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
- "@helia/verified-fetch": "^0.0.0-6c88ee1",
+ "@helia/verified-fetch": "0.0.0-3851fe2",
+ "@sgtpooki/file-type": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
diff --git a/examples/helia-browser-react-verified-fetch/src/App.tsx b/examples/helia-browser-react-verified-fetch/src/App.tsx
index 85867b30..2335dbde 100644
--- a/examples/helia-browser-react-verified-fetch/src/App.tsx
+++ b/examples/helia-browser-react-verified-fetch/src/App.tsx
@@ -1,25 +1,182 @@
-import { useCallback, useState } from 'react'
+// import { verifiedFetch, createVerifiedFetch } from '@helia/verified-fetch'
import { verifiedFetch } from '@helia/verified-fetch'
+import { fileTypeFromBuffer } from '@sgtpooki/file-type'
+import { useCallback, useState } from 'react'
import { helpText } from './constants'
-function App() {
+function renderOutput (output: string | JSX.Element, err: string): JSX.Element {
+ console.log('err: ', err)
+ console.log('output: ', output)
+ if (err.length > 0) {
+ return (
+
+ )
+ }
+
+ if (typeof output === 'string') {
+ return (
+
+ {output != null && (
+
+ {`${output}`}
+
+ )}
+
+ )
+ }
+
+ return output
+}
+
+function loadingIndicator (message: string): JSX.Element {
+ return (
+
+ )
+}
+
+function App (): JSX.Element {
const [path, setPath] = useState('')
- const [output, setOutput] = useState('')
+ const [output, setOutput] = useState('')
const [err, setErr] = useState('')
+ const [loading, setLoadingTo] = useState(null)
+
+ const setSuccess = useCallback((message: string | JSX.Element) => {
+ setOutput(message)
+ setLoadingTo(null)
+ setErr('')
+ }, [])
+ const setError = useCallback((message: string) => {
+ setOutput('')
+ setLoadingTo(null)
+ setErr(message)
+ }, [])
+ const setLoading = useCallback((message: string) => {
+ setErr('')
+ setLoadingTo(loadingIndicator(message))
+ }, [])
+
+ const handleImageType = useCallback(async (resp: Response) => {
+ try {
+ setLoading('Waiting for full image data...')
+ const blob = await resp.blob()
+ const url = URL.createObjectURL(blob)
+ setSuccess()
+ } catch (err) {
+ setError((err as Error).message)
+ }
+ }, [])
+
+ const handleJsonType = useCallback(async (resp: Response) => {
+ try {
+ setLoading('Waiting for full JSON data...')
+ const json = await resp.json()
+ setSuccess(JSON.stringify(json, null, 2))
+ } catch (err) {
+ setError((err as Error).message)
+
+ }
+ }, [])
+
+ const handleVideoType = useCallback(async (resp: Response) => {
+ try {
+ setLoading('Waiting for full video data...')
+ const blob = await resp.blob()
+ const url = URL.createObjectURL(blob)
+ setSuccess()
+ } catch (err) {
+ setError((err as Error).message)
+ }
+ }, [])
const onFetchJson = useCallback(async () => {
- if (!path) {
- setErr('Invalid path')
+ if (path == null) {
+ setError('Invalid path')
return
}
- const resp = await verifiedFetch(path)
- const json = await resp.json()
+ try {
+ setLoading('Fetching json response...')
+ const resp = await verifiedFetch(path)
+ await handleJsonType(resp)
+ } catch (err) {
+ setError((err as Error).message)
+ }
+
+ }, [path, handleJsonType])
- setOutput(json)
+ const onFetchImage = useCallback(async () => {
+ if (path == null) {
+ setError('Invalid path')
+ return
+ }
+ try {
+ setLoading('Fetching image response...')
+ const resp = await verifiedFetch(path)
+ await handleImageType(resp)
+ } catch (err) {
+ setError((err as Error).message)
+ }
+ }, [path, handleImageType])
+
+ const onFetchFile = useCallback(async () => {
+ if (path == null) {
+ setError('Invalid path')
+ return
+ }
+ try {
+ setLoading('Fetching content to download...')
+ const resp = await verifiedFetch(path)
+ const blob = await resp.blob()
+ const url = URL.createObjectURL(blob)
+ const downloadLink = document.createElement('a')
+ downloadLink.href = url
+ downloadLink.download = 'download'
+ setSuccess('') // clear output
+ downloadLink.click()
+ } catch (err) {
+ setError((err as Error).message)
+ }
}, [path])
- const onFetchImage = useCallback(async () => {}, [path])
- const onFetchFile = useCallback(async () => {}, [path])
+ const onFetchAuto = useCallback(async () => {
+ if (path == null) {
+ setError('Invalid path')
+ return
+ }
+ try {
+ setLoading('Fetching auto content...')
+ const resp = await verifiedFetch(path)
+ const buffer = await resp.clone().arrayBuffer()
+ let contentType = (await fileTypeFromBuffer(new Uint8Array(buffer)))?.mime
+ if (contentType == null) {
+ try {
+ // see if we can parse as json
+ await resp.clone().json()
+ contentType = 'application/json'
+ } catch (err) {
+ // ignore
+ }
+ }
+ switch (true) {
+ case contentType.includes('image'):
+ await handleImageType(resp)
+ break
+ case contentType.includes('json'):
+ await handleJsonType(resp)
+ break
+ case contentType.includes('video'):
+ await handleVideoType(resp)
+ break
+ default:
+ setError(`Unknown content-type: ${contentType}`)
+ }
+ } catch (err) {
+ setError((err as Error).message)
+ }
+ }, [path, handleImageType, handleJsonType, handleVideoType])
return (
@@ -37,7 +194,7 @@ function App() {
type="text"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="ipfs://... or ipns://"
- onChange={(e) => setPath(e.target.value)}
+ onChange={(e) => { setPath(e.target.value) }}
value={path}
/>
+
{helpText}
@@ -67,13 +231,7 @@ function App() {
{/* Left */}
{/* Right */}
-
- {output && (
-
-
-
- )}
-
+ {renderOutput(loading ?? output, err)}