Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 57 additions & 6 deletions packages/image/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
# Minima Image Handbook
# Minima Custom Token Image Processor

This package is made to facilitate image composability across the Minima dApp eco-system.
This package is made to facilitate image compatibility across the Minima dApp ecosystem. It holds both a custom React component, `Image`, and a utility methods that allow you to compress, process and parse custom token images seamlessly.

It has both a re-usable React component labelled `Image` and utility methods that can be used independently especially useful for processing, compressing, and fetching images from various sources, including base64 encodings and IPFS/IPNS.

The `Image` tag that can be imported by `import { Image } from "@minima-global/image"` and then using it like:
## Installation
Install `image` using your preferred package manager:

```bash
npm install @minima-global/image
```

` <Image src={(minimaToken.url} /> `

Otherwise you can have a look at how the Image component was built using the utility functions in the `/utils` folder part of the package.
## Features
- Re-usable React Component, Image
- Utility methods that allow you to compress, parse and process custom token images depending on their format
- It currently handles base64 encoding, IPFS, IPNS and external URLS.

## Documentation

View the full documentation on [docs.minima.global](https://docs.minima.global/docs/development/using-typescript).

## Usage

```tsx
import { Image } from "@minima-global/image";

... rest of your component

<Image src={customToken.url} />
```

Independently you have a suite of utility methods that can do things like,

```tsx
import { DEFAULT_BASE64_IMAGE, fetchIPFSImage, getBase64Image, isBase64Image, isIPFSOrIPNS, isValidUrl } from "@minima-global/image" // [!code highlight]

... rest of your component

if (isIPFSOrIPNS(src)) {
const ipfsUrl = await fetchIPFSImage(src)
return ipfsUrl;
} else if (isValidUrl(src)) {
return src;
} else if (isBase64Image(src)) {
const completeBase64Image = getBase64Image(src)
return completeBase64Image
}

... rest of your component
```

You can also compress an image to ensure it fits on-chain and doesn't cause issues for you when making transactions

```tsx
import { compressImage } from "@minima-global/image";

const compressedImage = await compressImage(tokensBase64ImageData);
```


## Contributing

This project is made better by contributors like you, and we welcome contributions of all sizes - from fixing typos, adding new features and fixing types, please open an issue or submit a pull request, but be sure to read the [contributing guidelines](https://github.com/minima-global/dev-tools/blob/main/CONTRIBUTING.md).
2 changes: 1 addition & 1 deletion packages/image/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@minima-global/image",
"version": "1.5.4",
"version": "1.5.5",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand Down
69 changes: 21 additions & 48 deletions packages/image/src/components/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ interface ImageProps {
src: string
alt?: string
className?: string
loader?: React.ReactNode | false
}

type ImageType = "on-chain" | "external_url" | "ipfs" | "default"

const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token image" }) => {
const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token image", loader = false }) => {
const [imageData, setImageData] = useState(DEFAULT_BASE64_IMAGE)
const [imageType, setImageType] = useState<ImageType>("default")
const [isLoading, setIsLoading] = useState(true)

useEffect(() => {
Expand All @@ -21,22 +19,17 @@ const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token
if (isIPFSOrIPNS(src)) {
try {
const ipfsUrl = await fetchIPFSImage(src)
setImageType("ipfs")
setImageData(ipfsUrl)
} catch (error) {
console.error("Failed to fetch IPFS image:", error)
setImageType("default")
setImageData(DEFAULT_BASE64_IMAGE)
}
} else if (isValidUrl(src)) {
setImageType("external_url")
setImageData(src)
} else if (isBase64Image(src)) {
const completeBase64Image = getBase64Image(src)
setImageType("on-chain")
setImageData(completeBase64Image)
} else {
setImageType("default")
setImageData(DEFAULT_BASE64_IMAGE)
}
setIsLoading(false)
Expand All @@ -45,23 +38,6 @@ const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token
processImageSource()
}, [src])

const getWrapperStyle = (): React.CSSProperties => {
const baseStyle: React.CSSProperties = {
position: "relative",
overflow: "hidden",
}

switch (imageType) {
case "on-chain":
return { ...baseStyle, width: "50px", height: "50px" }
case "external_url":
case "ipfs":
return { ...baseStyle, width: "100%", maxWidth: "300px", height: "300px" }
default:
return { ...baseStyle, width: "100px", height: "100px" }
}
}

const loadingStyle: React.CSSProperties = {
position: "absolute",
top: 0,
Expand All @@ -75,30 +51,27 @@ const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token
animation: "pulse 1.5s infinite ease-in-out",
}

const imageStyle: React.CSSProperties = {
width: "100%",
height: "100%",
objectFit: "contain",
padding: imageType === "on-chain" ? "4px" : "0",
}

return (
<div style={getWrapperStyle()} className={className}>
<>
{isLoading ? (
<div style={loadingStyle}>
<div
style={{
width: "20px",
height: "20px",
borderRadius: "50%",
border: "2px solid #e2e8f0",
borderTopColor: "#3b82f6",
animation: "spin 1s linear infinite",
}}
/>
</div>
<>
{!loader &&
<div style={loadingStyle}>
<div
style={{
width: "20px",
height: "20px",
borderRadius: "50%",
border: "2px solid #e2e8f0",
borderTopColor: "#3b82f6",
animation: "spin 1s linear infinite",
}}
/></div>}

{loader && loader}
</>
) : (
<img src={imageData || "/placeholder.svg"} alt={alt} style={imageStyle} />
<img src={imageData || "/placeholder.svg"} alt={alt} className={className} />
)}
<style>{`
@keyframes pulse {
Expand All @@ -110,7 +83,7 @@ const Image: React.FC<ImageProps> = ({ src, className = "", alt = "Custom token
100% { transform: rotate(360deg); }
}
`}</style>
</div>
</>
)
}

Expand Down