Skip to content

Commit

Permalink
Merge pull request #287 from omnisat/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
hathbanger authored Dec 11, 2024
2 parents a695060 + cfa7454 commit de1ab3a
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 134 deletions.
10 changes: 7 additions & 3 deletions apps/demo.lasereyes.build/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ const App = ({ setNetwork }: { setNetwork: (n: NetworkType) => void }) => {
colors[Math.floor(Math.random() * 5)]
)

const pickRandomColor = () => {
setSelectedColor(colors[Math.floor(Math.random() * 5)])
}

const switchN = () => {
try {
Expand Down Expand Up @@ -110,6 +107,13 @@ const App = ({ setNetwork }: { setNetwork: (n: NetworkType) => void }) => {
})
}, [])


// useEffect(() => {
// if (address) {
// getInscriptions(0, 10).then((response) => setInscriptions(response.list))
// }
// }, [address])

useEffect(() => {
setSignature('')
setUnsignedPsbt(undefined)
Expand Down
300 changes: 300 additions & 0 deletions apps/demo.lasereyes.build/components/NFT.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
/* eslint-disable */
import React, { ReactNode, useEffect, useRef, useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { usePathname } from "next/navigation";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { ImNewTab } from "react-icons/im";
import { cn } from "@/lib/utils";
import { ContentType } from "@omnisat/lasereyes";

const Inscription = ({
contentUrl,
contentType,
size,
className,
children,
}: {
contentUrl: string;
contentType: ContentType;
size: number;
children?: ReactNode;
className?: string;
}) => {
const pathname = usePathname();
const canvasRef = useRef<HTMLCanvasElement>(null);
const [svgContent, setSvgContent] = useState<string | null>(null);

const getMimeType = (contentType: ContentType) =>
contentType.split(";")[0].trim().toLowerCase();

const isImageContentType = (contentType: ContentType) =>
getMimeType(contentType).startsWith("image/") &&
getMimeType(contentType) !== "image/gif" &&
getMimeType(contentType) !== "image/svg+xml";

const isGifContentType = (contentType: ContentType) =>
getMimeType(contentType) === "image/gif";

const isSvgContentType = (contentType: ContentType) =>
getMimeType(contentType) === "image/svg+xml";

const isHtmlContentType = (contentType: ContentType) =>
getMimeType(contentType) === "text/html";

useEffect(() => {
if (
isSvgContentType(contentType)
) {
fetch(contentUrl)
.then((response) => response.text())
.then((svg) => {
setSvgContent(svg);
})
.catch((error) => {
console.error("Error fetching SVG content:", error);
});
}
}, [contentUrl]);

useEffect(() => {
if (
contentUrl &&
contentType &&
isImageContentType(contentType)
) {
const canvas = canvasRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
const img = new window.Image();
img.src = contentUrl;
img.onload = () => {
const maxCanvasSize = size;
const aspectRatio = img.width / img.height;

let canvasWidth, canvasHeight;

if (aspectRatio > 1) {
canvasWidth = maxCanvasSize;
canvasHeight = maxCanvasSize / aspectRatio;
} else if (aspectRatio < 1) {
canvasHeight = maxCanvasSize;
canvasWidth = maxCanvasSize * aspectRatio;
} else {
canvasWidth = maxCanvasSize;
canvasHeight = maxCanvasSize;
}

canvas.width = canvasWidth;
canvas.height = canvasHeight;

const roundRect = (
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
radius: number,
) => {
if (width < 2 * radius) radius = width / 2;
if (height < 2 * radius) radius = height / 2;
ctx.moveTo(x + radius, y);
ctx.arcTo(x + width, y, x + width, y + height, radius);
ctx.arcTo(x + width, y + height, x, y + height, radius);
ctx.arcTo(x, y + height, x, y, radius);
ctx.arcTo(x, y, x + width, y, radius);
};
if (ctx) {
ctx.imageSmoothingEnabled = false; // Disable image smoothing for pixelated effect
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas

// Create a rounded rectangle path
const radius = 0; // Adjust this value to change the corner radius
ctx.beginPath();
roundRect(ctx, 0, 0, canvas.width, canvas.height, radius);
ctx.closePath();
ctx.clip(); // Clip to the rounded rectangle

// Draw the image within the clipped area
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
canvas.style.display = "block";
};
}
}
}, [contentUrl, size]);

let content;

if (
contentUrl &&
contentType &&
isHtmlContentType(contentType)
) {
content = (
<iframe
src={contentUrl}
width={size}
height={size}
style={{ border: "none", borderRadius: "0.5rem" }}
sandbox="allow-scripts allow-same-origin"
/>
);
} else if (
contentUrl &&
contentType &&
isGifContentType(contentType)
) {
content = (
<img
src={contentUrl}
alt={"inscription"}
style={{
margin: "auto",
display: "block",
width: "120%",
height: "auto",
}}
/>
);
} else if (
contentUrl &&
contentType &&
isSvgContentType(contentType)
) {
content = svgContent ? (
<DynamicSvgDisplay
svgContent={svgContent}
baseUrl={new URL(contentUrl).origin} // Pass base URL
size={size}
/>
) : (
<div>Loading...</div>
);
} else if (
contentUrl &&
contentType &&
isImageContentType(contentType)
) {
content = (
<canvas
ref={canvasRef}
id="canvas"
style={{
// imageRendering: "pixelated",
margin: "10px auto",
// display: "none",
width: "100%",
height: "100%",
}}
/>
);
} else {
// Handle other content types or provide a fallback
content = (
<div
style={{
width: size - 20,
height: size,
display: "block",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#0e1a15",
color: "#fff",
}}
>
Unsupported content type
</div>
);
}

return (
<Card
className={cn(
`border h-full self-end w-full grow flex flex-col items-center hover:grow max-w-full mx-auto p-1 rounded justify-between`,
className,
)}
>
<CardHeader className="p-0 cursor-pointer transition-transform overflow-hidden justify-end hover:scale-105">
{content}
</CardHeader>
<CardContent
className={`p-0 w-full ${pathname === "/" ? "mt-0" : "mt-4"}`}
>
<div className="flex flex-col gap-1 w-full">
{children}
</div>
</CardContent>
</Card>
);
};


const DynamicSvgDisplay = ({
svgContent,
baseUrl,
size = 500,
}: {
svgContent: string;
baseUrl: string;
size?: number;
}) => {
const iframeRef = useRef<HTMLIFrameElement | null>(null);

useEffect(() => {
if (iframeRef.current && svgContent) {
// Create the HTML string with the <base> tag and the SVG content
const htmlContent = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG Display</title>
<base href="${baseUrl}">
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
svg {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
${svgContent}
</body>
</html>
`;

// Inject the HTML content into the iframe
const iframeDoc =
iframeRef.current.contentDocument ||
iframeRef.current.contentWindow?.document;
if (iframeDoc) {
iframeDoc.open();
iframeDoc.write(htmlContent);
iframeDoc.close();
}
}
}, [svgContent, baseUrl]);

return (
<iframe
ref={iframeRef}
width={size}
height={size}
style={{ border: "none" }}
title="Dynamic SVG Display"
/>
);
};

export default Inscription
2 changes: 1 addition & 1 deletion packages/lasereyes-core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@omnisat/lasereyes-core",
"private": false,
"version": "0.0.51",
"version": "0.0.52-rc.1",
"type": "module",
"main": "./dist/index.umd.cjs",
"module": "./dist/index.js",
Expand Down
7 changes: 5 additions & 2 deletions packages/lasereyes-core/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,16 @@ export class LaserEyesClient {
}
}

async getInscriptions() {
async getInscriptions(offset?: number, limit?: number) {
if (!this.$store.get().provider) return
if (this.$providerMap[this.$store.get().provider!]) {
try {
return await this.$providerMap[
this.$store.get().provider!
]?.getInscriptions()
]?.getInscriptions(
offset,
limit
)
} catch (error) {
if (error instanceof Error) {
if (error.message.toLowerCase().includes('not implemented')) {
Expand Down
13 changes: 7 additions & 6 deletions packages/lasereyes-core/src/client/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export abstract class WalletProvider {
this.initialize()
}

disconnect(): void {}
disconnect(): void { }

abstract initialize(): void

Expand Down Expand Up @@ -73,7 +73,8 @@ export abstract class WalletProvider {
return await getBTCBalance(paymentAddress, this.$network.get())
}

async getInscriptions(): Promise<any[]> {
async getInscriptions(offset?: number, limit?: number): Promise<any[]> {
console.log('getInscriptions not implemented', offset, limit)
throw UNSUPPORTED_PROVIDER_METHOD_ERROR
}

Expand All @@ -89,10 +90,10 @@ export abstract class WalletProvider {
broadcast?: boolean
): Promise<
| {
signedPsbtHex: string | undefined
signedPsbtBase64: string | undefined
txId?: string
}
signedPsbtHex: string | undefined
signedPsbtBase64: string | undefined
txId?: string
}
| undefined
>

Expand Down
Loading

0 comments on commit de1ab3a

Please sign in to comment.