Skip to content

Commit

Permalink
Merge pull request #197 from hackdays-io/feature/front-hooks-ipfs
Browse files Browse the repository at this point in the history
fix: IPFS upload
  • Loading branch information
yu23ki14 authored Dec 4, 2024
2 parents 5849dbd + 4c0825c commit 3e8d9fd
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 27 deletions.
4 changes: 3 additions & 1 deletion pkgs/frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ VITE_SPLITS_CREATOR_ADDRESS=0x9c3648df4bb82fdf067a9b083900a986f9b27e9a

VITE_PIMLICO_API_KEY=

VITE_PINATA_JWT=
VITE_PINATA_JWT=

VITE_PINATA_GATEWAY=
41 changes: 40 additions & 1 deletion pkgs/frontend/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Box } from "@chakra-ui/react";
import { Box, Input } from "@chakra-ui/react";
import type { MetaFunction } from "@remix-run/node";
import { CommonButton } from "~/components/common/CommonButton";
import { useBigBang } from "hooks/useBigBang";
import {
useUploadMetadataToIpfs,
useUploadImageFileToIpfs,
} from "hooks/useIpfs";

export const meta: MetaFunction = () => {
return [
Expand All @@ -12,6 +16,13 @@ export const meta: MetaFunction = () => {

export default function Index() {
const { bigbang, isLoading } = useBigBang();
const { uploadMetadataToIpfs, isLoading: isUploadingMetadataToIpfs } =
useUploadMetadataToIpfs();
const {
uploadImageFileToIpfs,
setImageFile,
isLoading: isUploadingImageFileToIpfs,
} = useUploadImageFileToIpfs();

const handleBigBang = async () => {
const res = await bigbang({
Expand All @@ -26,11 +37,39 @@ export default function Index() {
console.log(res);
};

const metadata = {
name: "Toban test",
description: "Toban test",
responsibilities: "Toban test",
authorities: "Toban test",
eligibility: true,
toggle: true,
};

return (
<Box textAlign="center" fontSize="xl" pt="30vh">
<CommonButton loading={isLoading} onClick={handleBigBang}>
BigBang
</CommonButton>
<CommonButton
loading={isUploadingMetadataToIpfs}
onClick={() => uploadMetadataToIpfs(metadata)}
>
Upload Metadata to IPFS
</CommonButton>
<Input
type="file"
accept="image/*"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setImageFile(e.target.files?.[0] || null)
}
/>
<CommonButton
loading={isUploadingImageFileToIpfs}
onClick={uploadImageFileToIpfs}
>
Upload Image File to IPFS
</CommonButton>
</Box>
);
}
68 changes: 43 additions & 25 deletions pkgs/frontend/hooks/useIpfs.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState } from "react";
import { PinataSDK } from "pinata-web3";
import { Readable } from "stream";
import { ipfsUploadJson, ipfsUploadFile } from "utils/ipfs";

export const useUploadMetadataToIpfs = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);

const uploadMetadataToIpfs = async ({
name,
Expand All @@ -19,13 +19,12 @@ export const useUploadMetadataToIpfs = () => {
authorities: string;
eligibility: boolean;
toggle: boolean;
}) => {
}): Promise<{ ipfsCid: string; ipfsUri: string } | null> => {
setIsLoading(true);
setError(null);

try {
const pinata = new PinataSDK({ pinataJwt: process.env.VITE_PINATA_JWT });

const upload = await pinata.upload.json({
const upload = await ipfsUploadJson({
type: "1.0",
data: {
name,
Expand All @@ -37,45 +36,64 @@ export const useUploadMetadataToIpfs = () => {
},
});

console.log("CID:", upload.IpfsHash);
console.log("URI:", `ipfs://${upload.IpfsHash}`);
} catch (error) {
console.error(error);
const ipfsCid = upload.IpfsHash;
const ipfsUri = `ipfs://${ipfsCid}`;

console.log("Successfully uploaded metadata to IPFS");
console.log("IPFS CID:", ipfsCid);
console.log("IPFS URI:", ipfsUri);

return { ipfsCid, ipfsUri };
} catch (err) {
setError(
err instanceof Error ? err : new Error("Unknown error occurred")
);
return null;
} finally {
setIsLoading(false);
}
};

return { uploadMetadataToIpfs, isLoading };
return { uploadMetadataToIpfs, isLoading, error };
};

export const useUploadImageToIpfs = () => {
export const useUploadImageFileToIpfs = () => {
const [isLoading, setIsLoading] = useState(false);
const [imageFile, setImageFile] = useState<File | null>(null);
const [error, setError] = useState<Error | null>(null);

const uploadImageToIpfs = async () => {
if (!imageFile) return;
const uploadImageFileToIpfs = async (): Promise<{
ipfsCid: string;
ipfsUri: string;
} | null> => {
if (!imageFile || !imageFile.type.startsWith("image/")) {
setError(new Error("Invalid or no image file selected"));
return null;
}

setIsLoading(true);
setError(null);

try {
const pinata = new PinataSDK({ pinataJwt: process.env.VITE_PINATA_JWT });
const upload = await ipfsUploadFile(imageFile);

const buffer = await imageFile.arrayBuffer();
const stream = Readable.from(Buffer.from(buffer));
const ipfsCid = upload.IpfsHash;
const ipfsUri = `ipfs://${ipfsCid}`;

const upload = await pinata.upload.stream(stream, {
metadata: { name: `TobanFrontend_${new Date().getTime()}` },
});
console.log("Successfully uploaded image file to IPFS");
console.log("IPFS CID:", ipfsCid);
console.log("IPFS URI:", ipfsUri);

console.log("CID:", upload.IpfsHash);
console.log("URI:", `ipfs://${upload.IpfsHash}`);
} catch (error) {
console.error(error);
return { ipfsCid, ipfsUri };
} catch (err) {
setError(
err instanceof Error ? err : new Error("Unknown error occurred")
);
return null;
} finally {
setIsLoading(false);
}
};

return { uploadImageToIpfs, setImageFile, isLoading };
return { uploadImageFileToIpfs, imageFile, setImageFile, isLoading, error };
};
51 changes: 51 additions & 0 deletions pkgs/frontend/utils/ipfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { PinataSDK } from "pinata-web3";

const getPinataConfig = () => {
const pinataJwt = import.meta.env.VITE_PINATA_JWT;
const pinataGateway = import.meta.env.VITE_PINATA_GATEWAY;

if (!pinataJwt) {
throw new Error("VITE_PINATA_JWT is not defined");
}
if (!pinataGateway) {
throw new Error("VITE_PINATA_GATEWAY is not defined");
}

return { pinataJwt, pinataGateway };
};

let ipfsClient: PinataSDK | null = null;

export const createIpfsClient = () => {
if (ipfsClient) return ipfsClient;

const { pinataJwt, pinataGateway } = getPinataConfig();
ipfsClient = new PinataSDK({
pinataJwt,
pinataGateway,
});

return ipfsClient;
};

export const ipfsUploadJson = async (object: object) => {
try {
const ipfsClient = createIpfsClient();
const upload = await ipfsClient.upload.json(object);
return upload;
} catch (error) {
console.error("Failed to upload JSON to IPFS:", error);
throw error;
}
};

export const ipfsUploadFile = async (file: File) => {
try {
const ipfsClient = createIpfsClient();
const upload = await ipfsClient.upload.file(file);
return upload;
} catch (error) {
console.error("Failed to upload file to IPFS:", error);
throw error;
}
};

0 comments on commit 3e8d9fd

Please sign in to comment.