Skip to content

Commit 96a49cc

Browse files
committed
chore(web): new-ipfs-upload-flow
1 parent 4fa24cb commit 96a49cc

File tree

10 files changed

+65
-63
lines changed

10 files changed

+65
-63
lines changed

web/.env.devnet-neo.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/6
55
export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge
66
export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598
77
export REACT_APP_ARBITRATOR_TYPE=neo
8-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
8+
export REACT_APP_ATLAS_URI=http://localhost:3000
99
export WALLETCONNECT_PROJECT_ID=
1010
export ALCHEMY_API_KEY=
1111
export NODE_OPTIONS='--max-old-space-size=7680'

web/.env.devnet-university.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/6
55
export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge
66
export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598
77
export REACT_APP_ARBITRATOR_TYPE=university
8-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
8+
export REACT_APP_ATLAS_URI=http://localhost:3000
99
export WALLETCONNECT_PROJECT_ID=
1010
export ALCHEMY_API_KEY=
1111
export NODE_OPTIONS='--max-old-space-size=7680'

web/.env.devnet.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export REACT_APP_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/klero
44
export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest
55
export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge
66
export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3084598
7-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
7+
export REACT_APP_ATLAS_URI=http://localhost:3000
88
export WALLETCONNECT_PROJECT_ID=
99
export ALCHEMY_API_KEY=
1010
export NODE_OPTIONS='--max-old-space-size=7680'

web/.env.local.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
export REACT_APP_DEPLOYMENT=devnet
33
export REACT_APP_CORE_SUBGRAPH=http://localhost:8000/subgraphs/name/kleros/kleros-v2-core-local
44
export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.thegraph.com/subgraphs/name/alcercu/templateregistrydevnet
5-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
5+
export REACT_APP_ATLAS_URI=http://localhost:3000
66
export WALLETCONNECT_PROJECT_ID=
77
export ALCHEMY_API_KEY=
88
export NODE_OPTIONS='--max-old-space-size=7680'

web/.env.mainnet-neo.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export REACT_APP_DRT_ARBMAINNET_SUBGRAPH=https://api.studio.thegraph.com/query/6
55
export REACT_APP_STATUS_URL=https://kleros-v2-devnet.betteruptime.com/badge
66
export REACT_APP_GENESIS_BLOCK_ARBMAINNET=190274403
77
export REACT_APP_ARBITRATOR_TYPE=neo
8-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
8+
export REACT_APP_ATLAS_URI=http://localhost:3000
99
export WALLETCONNECT_PROJECT_ID=
1010
export ALCHEMY_API_KEY=
1111
export NODE_OPTIONS='--max-old-space-size=7680'

web/.env.testnet.public

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export REACT_APP_DEPLOYMENT=testnet
33
export REACT_APP_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-core-testnet/version/latest
44
export REACT_APP_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest
55
export REACT_APP_STATUS_URL=https://kleros-v2.betteruptime.com/badge
6-
export REACT_APP_ATLAS_URI=http://localhost:3000/graphql
6+
export REACT_APP_ATLAS_URI=http://localhost:3000
77
export REACT_APP_GENESIS_BLOCK_ARBSEPOLIA=3842783
88
export WALLETCONNECT_PROJECT_ID=
99
export ALCHEMY_API_KEY=

web/src/context/AtlasProvider.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
type UpdateEmailData,
2121
type ConfirmEmailData,
2222
type ConfirmEmailResponse,
23+
Roles,
24+
Products,
2325
} from "utils/atlas";
2426

2527
import { isUndefined } from "src/utils";
@@ -36,7 +38,7 @@ interface IAtlasProvider {
3638
authoriseUser: () => void;
3739
addUser: (userSettings: AddUserData) => Promise<boolean>;
3840
updateEmail: (userSettings: UpdateEmailData) => Promise<boolean>;
39-
uploadFile: (file: File) => Promise<string | null>;
41+
uploadFile: (file: File, role: Roles) => Promise<string | null>;
4042
confirmEmail: (userSettings: ConfirmEmailData) => Promise<
4143
ConfirmEmailResponse & {
4244
isError: boolean;
@@ -69,7 +71,7 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
6971
authorization: `Bearer ${authToken}`,
7072
}
7173
: undefined;
72-
return new GraphQLClient(atlasUri, { headers });
74+
return new GraphQLClient(`${atlasUri}/graphql`, { headers });
7375
}, [authToken]);
7476

7577
/**
@@ -215,17 +217,20 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
215217
/**
216218
* @description upload file to ipfs
217219
* @param {File} file - file to be uploaded
220+
* @param {Roles} role - role for which file is being uploaded
218221
* @returns {Promise<string | null>} A promise that resolves to the ipfs cid if file was uploaded successfully else
219222
* null
220223
*/
221224
const uploadFile = useCallback(
222-
async (file: File) => {
225+
async (file: File, role: Roles) => {
223226
try {
224-
if (!address || !isVerified) return null;
227+
if (!address || !isVerified || !atlasUri || !authToken) return null;
225228
setIsUploadingFile(true);
226229

227-
const hash = await uploadToIpfs(atlasGqlClient, file);
228-
230+
const hash = await uploadToIpfs(
231+
{ baseUrl: atlasUri, authToken },
232+
{ file, name: file.name, role, product: Products.CourtV2 }
233+
);
229234
return hash ? `/ipfs/${hash}` : null;
230235
} catch (err: any) {
231236
// eslint-disable-next-line
@@ -235,7 +240,7 @@ const AtlasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) =
235240
setIsUploadingFile(false);
236241
}
237242
},
238-
[address, isVerified, setIsUploadingFile, atlasGqlClient]
243+
[address, isVerified, setIsUploadingFile, authToken]
239244
);
240245

241246
/**

web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Textarea, Button, FileUploader } from "@kleros/ui-components-library";
99

1010
import { useAtlasProvider } from "context/AtlasProvider";
1111
import { simulateEvidenceModuleSubmitEvidence } from "hooks/contracts/generated";
12+
import { Roles } from "utils/atlas";
1213
import { wrapWithToast, OPTIONS as toastOptions } from "utils/wrapWithToast";
1314

1415
import EnsureAuth from "components/EnsureAuth";
@@ -101,11 +102,15 @@ const SubmitEvidenceModal: React.FC<{
101102
);
102103
};
103104

104-
const constructEvidence = async (uploadFile: (file: File) => Promise<string | null>, msg: string, file?: File) => {
105+
const constructEvidence = async (
106+
uploadFile: (file: File, role: Roles) => Promise<string | null>,
107+
msg: string,
108+
file?: File
109+
) => {
105110
let fileURI: string | null = null;
106111
if (file) {
107112
toast.info("Uploading to IPFS", toastOptions);
108-
fileURI = await uploadFile(file);
113+
fileURI = await uploadFile(file, Roles.Evidence);
109114
if (!fileURI) throw new Error("Error uploading evidence file");
110115
}
111116
return { name: "Evidence", description: msg, fileURI };

web/src/pages/Resolver/Policy/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { FileUploader } from "@kleros/ui-components-library";
77

88
import { useAtlasProvider } from "context/AtlasProvider";
99
import { useNewDisputeContext } from "context/NewDisputeContext";
10+
import { Roles } from "utils/atlas";
1011
import { OPTIONS as toastOptions } from "utils/wrapWithToast";
1112

1213
import { landscapeStyle } from "styles/landscapeStyle";
@@ -57,7 +58,7 @@ const Policy: React.FC = () => {
5758
setIsPolicyUploading(true);
5859
toast.info("Uploading Policy to IPFS", toastOptions);
5960

60-
uploadFile(file)
61+
uploadFile(file, Roles.Policy)
6162
.then(async (cid) => {
6263
if (!cid) return;
6364
setDisputeData({ ...disputeData, policyURI: cid });

web/src/utils/atlas/uploadToIpfs.ts

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
1-
import { gql, type GraphQLClient } from "graphql-request";
21
import { toast } from "react-toastify";
32

43
import { OPTIONS } from "utils/wrapWithToast";
54

6-
export async function uploadToIpfs(client: GraphQLClient, file: File): Promise<string | null> {
7-
const presignedUrl = await getPreSignedUrl(client, file.name);
5+
export enum Products {
6+
CourtV2 = "CourtV2",
7+
}
8+
9+
export enum Roles {
10+
Photo = "photo",
11+
Evidence = "evidence",
12+
Policy = "policy",
13+
Generic = "generic",
14+
}
15+
16+
export type IpfsUploadPayload = {
17+
file: File;
18+
name: string;
19+
product: Products;
20+
role: Roles;
21+
};
22+
23+
type Config = {
24+
baseUrl: string;
25+
authToken: string;
26+
};
27+
28+
export async function uploadToIpfs(config: Config, payload: IpfsUploadPayload): Promise<string | null> {
29+
const formData = new FormData();
30+
formData.append("file", payload.file, payload.name);
31+
formData.append("name", payload.name);
32+
formData.append("product", payload.product);
33+
formData.append("role", payload.role);
834

935
return toast.promise<string | null, Error>(
10-
fetch(presignedUrl, {
11-
method: "PUT",
12-
body: file,
36+
fetch(`${config.baseUrl}/ipfs/file`, {
37+
method: "POST",
38+
headers: {
39+
authorization: `Bearer ${config.authToken}`,
40+
},
41+
body: formData,
1342
}).then(async (response) => {
14-
if (response.status !== 200) {
43+
if (!response.ok) {
1544
const error = await response.json().catch(() => ({ message: "Error uploading to IPFS" }));
1645
throw new Error(error.message);
1746
}
18-
return response.headers.get("x-amz-meta-cid");
47+
48+
return await response.text();
1949
}),
2050
{
2151
pending: `Uploading to IPFS...`,
@@ -29,42 +59,3 @@ export async function uploadToIpfs(client: GraphQLClient, file: File): Promise<s
2959
OPTIONS
3060
);
3161
}
32-
33-
const presignedUrlQuery = gql`
34-
mutation GetPresignedUrl($filename: String!) {
35-
getPresignedUrl(filename: $filename, appname: KlerosCourt)
36-
}
37-
`;
38-
39-
type GetPresignedUrlResponse = {
40-
getPresignedUrl: string;
41-
};
42-
43-
const getPreSignedUrl = (client: GraphQLClient, filename: string) => {
44-
const variables = {
45-
filename,
46-
};
47-
48-
return toast.promise<string, Error>(
49-
client
50-
.request<GetPresignedUrlResponse>(presignedUrlQuery, variables)
51-
.then(async (response) => response.getPresignedUrl)
52-
.catch((errors) => {
53-
// eslint-disable-next-line no-console
54-
console.log("Get Presigned Url error:", { errors });
55-
56-
const errorMessage = Array.isArray(errors?.response?.errors)
57-
? errors.response.errors[0]?.message
58-
: "Unknown error";
59-
throw new Error(errorMessage);
60-
}),
61-
{
62-
error: {
63-
render({ data: error }) {
64-
return `Getting Presigned Url failed: ${error?.message}`;
65-
},
66-
},
67-
},
68-
OPTIONS
69-
);
70-
};

0 commit comments

Comments
 (0)