-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Kelvin Steiner <[email protected]> Co-authored-by: PsicoThePato <[email protected]>
- Loading branch information
1 parent
ffd29da
commit 56d2223
Showing
6 changed files
with
307 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
223 changes: 223 additions & 0 deletions
223
apps/commune-governance/src/app/components/proposal/register-module.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
import { useState } from "react"; | ||
import { useRouter } from "next/navigation"; | ||
import MarkdownPreview from "@uiw/react-markdown-preview"; | ||
import { z } from "zod"; | ||
|
||
import type { TransactionResult } from "@commune-ts/types"; | ||
import { useCommune } from "@commune-ts/providers/use-commune"; | ||
import { toast } from "@commune-ts/providers/use-toast"; | ||
import { | ||
Button, | ||
Input, | ||
Label, | ||
Separator, | ||
Tabs, | ||
TabsContent, | ||
TabsList, | ||
TabsTrigger, | ||
Textarea, | ||
TransactionStatus, | ||
} from "@commune-ts/ui"; | ||
|
||
import { cairo } from "~/utils/fonts"; | ||
|
||
const moduleSchema = z.object({ | ||
title: z.string().min(1, "Title is required"), | ||
body: z.string().min(1, "Body is required"), | ||
}); | ||
|
||
export function RegisterModule(): JSX.Element { | ||
const router = useRouter(); | ||
const { isConnected, registerModule, balance } = useCommune(); | ||
|
||
const [subnetName, setSubnetName] = useState(""); | ||
const [address, setAddress] = useState(""); | ||
const [name, setName] = useState(""); | ||
const [moduleId, setModuleId] = useState(""); | ||
|
||
const [title, setTitle] = useState(""); | ||
const [body, setBody] = useState(""); | ||
|
||
const [uploading, setUploading] = useState(false); | ||
const [activeTab, setActiveTab] = useState("edit"); | ||
|
||
const [transactionStatus, setTransactionStatus] = useState<TransactionResult>( | ||
{ | ||
status: null, | ||
message: null, | ||
finalized: false, | ||
}, | ||
); | ||
|
||
function handleCallback(callbackReturn: TransactionResult): void { | ||
setTransactionStatus(callbackReturn); | ||
} | ||
|
||
async function uploadFile(fileToUpload: File): Promise<void> { | ||
try { | ||
setUploading(true); | ||
const data = new FormData(); | ||
data.set("file", fileToUpload); | ||
const res = await fetch("/api/files", { | ||
method: "POST", | ||
body: data, | ||
}); | ||
const ipfs = (await res.json()) as { IpfsHash: string }; | ||
setUploading(false); | ||
|
||
if (ipfs.IpfsHash === "undefined" || !ipfs.IpfsHash) { | ||
toast.error("Error uploading transfer dao treasury moduleCost"); | ||
return; | ||
} | ||
|
||
if (!balance) { | ||
toast.error("Balance is still loading"); | ||
return; | ||
} | ||
|
||
const moduleCost = 2000; | ||
|
||
if (Number(balance) > moduleCost) { | ||
void registerModule({ | ||
subnetName, | ||
address, | ||
name, | ||
moduleId, | ||
metadata: `ipfs://${ipfs.IpfsHash}`, | ||
callback: handleCallback, | ||
}); | ||
} else { | ||
toast.error( | ||
`Insufficient balance to create module. Required: ${moduleCost} but got ${balance}`, | ||
); | ||
setTransactionStatus({ | ||
status: "ERROR", | ||
finalized: true, | ||
message: "Insufficient balance", | ||
}); | ||
} | ||
router.refresh(); | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
} catch (e) { | ||
setUploading(false); | ||
toast.error("Error uploading module"); | ||
} | ||
} | ||
|
||
function HandleSubmit(event: React.FormEvent<HTMLFormElement>): void { | ||
event.preventDefault(); | ||
setTransactionStatus({ | ||
status: "STARTING", | ||
finalized: false, | ||
message: "Starting module creation...", | ||
}); | ||
|
||
const result = moduleSchema.safeParse({ | ||
title, | ||
body, | ||
}); | ||
|
||
if (!result.success) { | ||
toast.error(result.error.errors.map((e) => e.message).join(", ")); | ||
setTransactionStatus({ | ||
status: "ERROR", | ||
finalized: true, | ||
message: "Error on form validation", | ||
}); | ||
return; | ||
} | ||
|
||
const moduleData = JSON.stringify({ | ||
title, | ||
body, | ||
}); | ||
const blob = new Blob([moduleData], { type: "application/json" }); | ||
const fileToUpload = new File([blob], "proposal.json", { | ||
type: "application/json", | ||
}); | ||
void uploadFile(fileToUpload); | ||
} | ||
|
||
return ( | ||
<form | ||
onSubmit={HandleSubmit} | ||
className="flex flex-col gap-4 text-green-500" | ||
> | ||
<Tabs value={activeTab} onValueChange={setActiveTab}> | ||
<TabsList> | ||
<TabsTrigger value="edit">Edit Content</TabsTrigger> | ||
<TabsTrigger value="preview">Preview Content</TabsTrigger> | ||
</TabsList> | ||
<TabsContent value="edit" className="flex flex-col gap-3"> | ||
<Input | ||
onChange={(e) => setName(e.target.value)} | ||
placeholder="Module Name (eg. ren-labs)" | ||
type="text" | ||
value={name} | ||
/> | ||
<Input | ||
onChange={(e) => setModuleId(e.target.value)} | ||
placeholder="Module ID (SS58 Address)" | ||
type="text" | ||
value={moduleId} | ||
/> | ||
<Input | ||
onChange={(e) => setSubnetName(e.target.value)} | ||
placeholder="Subnet Name (eg. General)" | ||
type="text" | ||
value={subnetName} | ||
/> | ||
<Input | ||
onChange={(e) => setAddress(e.target.value)} | ||
placeholder="Address (eg. 0.0.0.0:8000)" | ||
type="text" | ||
value={address} | ||
/> | ||
<Separator /> | ||
<Input | ||
onChange={(e) => setTitle(e.target.value)} | ||
placeholder="Your Module title here..." | ||
type="text" | ||
value={title} | ||
/> | ||
<Textarea | ||
onChange={(e) => setBody(e.target.value)} | ||
placeholder="Your module body here... (Markdown supported / HTML tags are not supported)" | ||
rows={5} | ||
value={body} | ||
/> | ||
</TabsContent> | ||
<TabsContent value="preview" className="bg-muted p-4"> | ||
{body ? ( | ||
<MarkdownPreview | ||
className={`${cairo.className} max-h-[40vh] overflow-auto`} | ||
source={`# ${title}\n${body}`} | ||
style={{ | ||
backgroundColor: "transparent", | ||
color: "white", | ||
}} | ||
/> | ||
) : ( | ||
<Label className="text-sm text-white"> | ||
Fill the body to preview here :) | ||
</Label> | ||
)} | ||
</TabsContent> | ||
</Tabs> | ||
<Button | ||
size="xl" | ||
type="submit" | ||
variant="default-green" | ||
disabled={!isConnected} | ||
> | ||
{uploading ? "Uploading..." : "Submit Module"} | ||
</Button> | ||
{transactionStatus.status && ( | ||
<TransactionStatus | ||
status={transactionStatus.status} | ||
message={transactionStatus.message} | ||
/> | ||
)} | ||
</form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.