From e9073931ae8cd4efb973249e02f7db44d6573923 Mon Sep 17 00:00:00 2001 From: mario4tier Date: Mon, 2 Sep 2024 16:43:56 -0400 Subject: [PATCH] Update demo walrus site --- .../packages/frontend/package.json | 3 +- .../packages/frontend/src/components/App.tsx | 168 +++++++++--------- .../frontend/src/components/BlobIdInput.tsx | 41 +++++ .../frontend/src/components/DemoImage.tsx | 89 ++++++++++ .../frontend/src/components/LabeledLink.tsx | 25 +++ .../src/components/LabeledLinkSuiftly.tsx | 21 +++ .../packages/frontend/src/styles/index.css | 38 +++- 7 files changed, 295 insertions(+), 90 deletions(-) create mode 100644 dapps/suiftly.walrus.site/packages/frontend/src/components/BlobIdInput.tsx create mode 100644 dapps/suiftly.walrus.site/packages/frontend/src/components/DemoImage.tsx create mode 100644 dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLink.tsx create mode 100644 dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLinkSuiftly.tsx diff --git a/dapps/suiftly.walrus.site/packages/frontend/package.json b/dapps/suiftly.walrus.site/packages/frontend/package.json index 41e79b1..105c8e6 100644 --- a/dapps/suiftly.walrus.site/packages/frontend/package.json +++ b/dapps/suiftly.walrus.site/packages/frontend/package.json @@ -29,7 +29,8 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", - "react-use": "^17.5.1" + "react-use": "^17.5.1", + "react-router-dom": "^6.22.0" }, "devDependencies": { "@types/node": "^20.14.15", diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/components/App.tsx b/dapps/suiftly.walrus.site/packages/frontend/src/components/App.tsx index 27274e1..77624e3 100644 --- a/dapps/suiftly.walrus.site/packages/frontend/src/components/App.tsx +++ b/dapps/suiftly.walrus.site/packages/frontend/src/components/App.tsx @@ -1,118 +1,114 @@ -import { FC, useEffect, useRef } from 'react' +import { FC, useState } from 'react' +import Layout from '~~/components/layout/Layout' { - /*import GreetingForm from '~~/components/GreetingForm'*/ + /*import NetworkSupportChecker from './NetworkSupportChecker'*/ } -import Layout from '~~/components/layout/Layout' -import NetworkSupportChecker from './NetworkSupportChecker' import { fetchBlob } from '@suiftly/core' - -// A fetchBlob() async function that take a single "blobID" string argument. -// It should return a promise that resolves to the blob data (a standard JS -// Blob interface). -// -// The blob data is first retreived with a CDN URL like this: -// https://cdn.suiftly.io/blobs/{blobID} -// -// The function should extract the Content-Type header and use it -// to build the JS Blob object. -// -// The function should throw an error if the response status is not 200. -// -// Example usage: -// const blob = await fetchBlob('some-blob-id') +import DemoImage from './DemoImage' +import { Link, Text, Flex } from '@radix-ui/themes' +import LabeledLinkSuiftly from './LabeledLinkSuiftly' +import BlobIdInput from './BlobIdInput' +import LabeledLink from './LabeledLink' const App: FC = () => { - // TODO Just proof-of-concept... need to design this way better!!!! - const fetchInitiated1 = useRef(false) - const fetchInitiated2 = useRef(false) - - useEffect(() => { - const blobID = 'fK7v0bft1JqVbxQaM_KJAYkejbY9FgU9doqZwg7smw8' - const imageContainer1 = document.getElementById('image-container-walrus') - const imageContainer2 = document.getElementById('image-container-suiftly') + const defaultBlobId = 'fK7v0bft1JqVbxQaM_KJAYkejbY9FgU9doqZwg7smw8' + const [blobId, setBlobId] = useState(defaultBlobId) - if (imageContainer1 && !fetchInitiated1.current) { - fetchInitiated1.current = true - fetchBlob(blobID, { allowSuiftly: false }) + const handleRunCode = () => { + const imageContainer = document.getElementById('image-container') + if (imageContainer) { + fetchBlob(blobId) .then((blob) => { const url = URL.createObjectURL(blob) const img = document.createElement('img') img.src = url img.alt = 'Fetched Blob' - // Clear the loading message and append the image - if (imageContainer1) { - imageContainer1.innerHTML = '' - imageContainer1.appendChild(img) - } + imageContainer.innerHTML = '' + imageContainer.appendChild(img) }) .catch((error) => { - console.error('Error fetching walrus blob:', error) - if (imageContainer1) { - imageContainer1.innerHTML = '

Error loading walrus image

' + console.error('Error fetching walrus blob:', blobId, error) + if (imageContainer) { + imageContainer.innerHTML = '

Error loading walrus image

' } }) } + } - if (imageContainer2 && !fetchInitiated2.current) { - fetchInitiated2.current = true - fetchBlob(blobID, { allowSuiftly: true }) + const codeSnippet = ` + const imageContainer = document.getElementById('image-container') + if (imageContainer) { + const blobId = '${blobId}' + fetchBlob(blobId) .then((blob) => { - const url = URL.createObjectURL(blob) const img = document.createElement('img') - img.src = url + img.src = URL.createObjectURL(blob) img.alt = 'Fetched Blob' - - // Clear the loading message and append the image - if (imageContainer2) { - imageContainer2.innerHTML = '' - imageContainer2.appendChild(img) - } + imageContainer.appendChild(img) }) .catch((error) => { - console.error('Error fetching suiftly blob:', error) - if (imageContainer2) { - imageContainer2.innerHTML = '

Error loading suiftly image

' - } + console.error('Error fetching:', blobId, error) }) } - - // Cleanup URL object when component unmounts - return () => { - if (imageContainer1) { - const img = imageContainer1.querySelector('img') - if (img) { - URL.revokeObjectURL(img.src) - } - } - if (imageContainer2) { - const img = imageContainer2.querySelector('img') - if (img) { - URL.revokeObjectURL(img.src) - } - } - } - }) + ` return ( - -
- {/**/} - -
-

Loading image...

-
- -
-

Loading image...

+ {/**/} +
+
+

+ Suiftly Demo +

+
+ Optionally customize this demo with any image blob ID: + +
+
+

+ Direct Links (Trust your CDN) +

+ + + +
+

+ NPM Package (Trust your CDN... but verify) +

+ + Install + + @suiftly/core + {' '} + to fetch blobs with Suiftly to Walrus failover and blob integrity + checks. + + + +
+ Coming soon: Simpler react components like{' '} + <ImageBlob blobId="..."> +
+
+ + + +
) diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/components/BlobIdInput.tsx b/dapps/suiftly.walrus.site/packages/frontend/src/components/BlobIdInput.tsx new file mode 100644 index 0000000..f50243c --- /dev/null +++ b/dapps/suiftly.walrus.site/packages/frontend/src/components/BlobIdInput.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { TextField, Button, Flex } from '@radix-ui/themes' + +interface BlobIdInputProps { + blobId: string + defaultBlobId: string + setBlobId: (blobId: string) => void +} + +const BlobIdInput: React.FC = ({ + blobId, + defaultBlobId, + setBlobId, +}) => { + const handleReset = () => { + setBlobId(defaultBlobId) + } + + return ( + + {/**/} + setBlobId(e.target.value)} + placeholder={defaultBlobId} + style={{ width: '400px' }} // Set the width here + /> + {blobId !== defaultBlobId && ( + + )} + + ) +} + +export default BlobIdInput diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/components/DemoImage.tsx b/dapps/suiftly.walrus.site/packages/frontend/src/components/DemoImage.tsx new file mode 100644 index 0000000..e426756 --- /dev/null +++ b/dapps/suiftly.walrus.site/packages/frontend/src/components/DemoImage.tsx @@ -0,0 +1,89 @@ +import React, { useState } from 'react' +import { Button, Flex, Text } from '@radix-ui/themes' + +interface DemoImageProps { + code: string + onRunCode: () => void +} + +const DemoImage: React.FC = ({ code, onRunCode }) => { + const [imageDisplayed, setImageDisplayed] = useState(false) + + const handleRunCode = () => { + setImageDisplayed(true) + onRunCode() + } + + const handleClearImage = () => { + const imageContainer = document.getElementById('image-container') + if (imageContainer) { + const existing_img = imageContainer.querySelector('img') + if (existing_img) { + imageContainer.removeChild(existing_img) + } + } + setImageDisplayed(false) + } + + return ( + + +
+          {code}
+        
+
+ + + image-container + + + + {imageDisplayed ? ( + + ) : ( + + )} + + +
+ ) +} + +export default DemoImage diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLink.tsx b/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLink.tsx new file mode 100644 index 0000000..808d40d --- /dev/null +++ b/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLink.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { Flex, Text, Link } from '@radix-ui/themes' + +interface LabeledLinkProps { + label: string + url: string + minWidthLabel?: string +} + +const LabeledLink: React.FC = ({ + label, + url, + minWidthLabel = '110px', +}) => { + return ( + + {label} : + + {url} + + + ) +} + +export default LabeledLink diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLinkSuiftly.tsx b/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLinkSuiftly.tsx new file mode 100644 index 0000000..c8e0430 --- /dev/null +++ b/dapps/suiftly.walrus.site/packages/frontend/src/components/LabeledLinkSuiftly.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { Flex, Text, Link } from '@radix-ui/themes' + +interface LabeledLinkProps { + label: string + blobId: string +} + +const LabeledLinkSuiftly: React.FC = ({ label, blobId }) => { + const url = `https://cdn.suiftly.io/${label}/${blobId}` + return ( + + {label} : + + https://cdn.suiftly.io/{label}/{blobId} + + + ) +} + +export default LabeledLinkSuiftly diff --git a/dapps/suiftly.walrus.site/packages/frontend/src/styles/index.css b/dapps/suiftly.walrus.site/packages/frontend/src/styles/index.css index 9d089fb..485c75a 100644 --- a/dapps/suiftly.walrus.site/packages/frontend/src/styles/index.css +++ b/dapps/suiftly.walrus.site/packages/frontend/src/styles/index.css @@ -18,11 +18,12 @@ -moz-osx-font-smoothing: grayscale; } -body, #root { +body, +#root { display: flex; flex-direction: column; - justify-content:center; - align-items:center; + justify-content: center; + align-items: center; min-width: 100%; min-height: 100vh; } @@ -40,3 +41,34 @@ body, #root { } } /* ~ Custom styles where it's not possible to use Tailwind classes. */ +.demo-image-container { + display: flex; + gap: 20px; +} + +.code-section { + flex: 1; +} + +.image-container { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #ccc; + width: 128px; + height: 128px; +} + +.run-code-button { + background-color: #007bff; + color: white; + padding: 10px; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.run-code-button:hover { + background-color: #0056b3; +}