import { IssueTrackerContext } from "@thorium/components/useIssueTracker"; import { toast } from "@thorium/context/ToastContext"; import Button from "@thorium/ui/Button"; import Input from "@thorium/ui/Input"; import MarkdownInput from "@thorium/ui/MarkdownInput"; import Modal from "@thorium/ui/Modal"; import randomWords from "@thorium/utils/random-words"; import { type Dispatch, type ReactNode, type SetStateAction, useEffect, useState } from "react"; import packageJson from "../../package.json" with { type: "json" }; import { useThoriumAccount } from "../context/ThoriumAccountContext"; import LoginButton from "./LoginButton"; import { Icon } from "./ui/Icon"; const availableLabels = ["Feature", "Bug", "Design", "Documentation", "Question", "Compliment"]; export const IssueTrackerProvider = ({ children }: { children: ReactNode }) => { const [open, setOpen] = useState(false); return ( {children} ); }; function IssueTracker({ open, setOpen, }: { open: boolean; setOpen: Dispatch>; }) { const { account, refresh } = useThoriumAccount(); const [body, setBody] = useState(""); const [invalid, setInvalid] = useState([]); useEffect( function revalidateOnVisibilityChange() { if (!open) return; function onVisibilityChange() { refresh(); } window.addEventListener("visibilitychange", onVisibilityChange); return () => window.removeEventListener("visibilitychange", onVisibilityChange); }, [refresh, open], ); useEffect( function revalidateOnFocus() { if (!open) return; function onFocus() { refresh(); } window.addEventListener("focus", onFocus); return () => window.removeEventListener("focus", onFocus); }, [refresh, open], ); return ( setOpen(open)} title="Submit an Issue"> {!account ? ( Sign in to Submit Issues To better track issues and collect feedback, you must be logged in to a Thorium Nova account. ) : !account.accounts?.includes("github") ? ( Connect to Github To submit issues, you must connect your Thorium Nova account to Github. Connect Account ) : ( { event.preventDefault(); const title = event.currentTarget.issueTitle.value; const label = event.currentTarget.label.value; let isInvalid = false; if (!title) { isInvalid = true; setInvalid((invalid) => invalid.concat("title")); } if (!label || label === "Select One") { isInvalid = true; setInvalid((invalid) => invalid.concat("type")); } if (isInvalid) { return; } const requestBody = { repo: "thorium-nova", title, body: `${body} ### Version: ${packageJson.version}`, labels: [label], }; setOpen(false); try { const response = await fetch(`${process.env.THORIUMSIM_URL}/api/issues`, { method: "POST", body: JSON.stringify(requestBody), headers: { "Content-Type": "application/json", Authorization: `Bearer ${account.access_token}`, }, }); const result = (await response.json()) as any; if (!response.ok || result.error) { toast({ title: "Error Submitting Issue", body: result.error || "An unknown error occurred", color: "error", }); return; } toast({ title: "Issue Submitted!", color: "success" }); setBody(""); } catch (err) { if (err instanceof Error) { toast({ title: "Error Submitting Issue", body: err.message || "An unknown error occurred", color: "error", }); } if (typeof err === "string") { toast({ title: "Error Submitting Issue", body: err || "An unknown error occurred", color: "error", }); } } }} > setInvalid(invalid.filter((i) => i !== "title"))} /> Body { setBody(newBody); }} name="body" saveImage={async function* saveImage(data: ArrayBuffer) { const uploadDataRequest = await fetch( `${process.env.THORIUMSIM_URL}/api/uploadData`, { headers: { Authorization: `Bearer ${account.access_token}`, }, }, ); const uploadData = (await uploadDataRequest.json()) as any; if (!uploadDataRequest.ok) { toast({ title: "Error Uploading File", body: uploadData.error, color: "error", }); throw new Error(uploadData.error); } const nameParts = randomWords(3); const name = `${ Array.isArray(nameParts) ? nameParts.join("-") : nameParts }-${Date.now()}`; const image = new File([new Blob([data])], `${name}.png`, { type: "image/png", }); const result = (await fetch(uploadData.uploadUrl, { method: "POST", body: image, headers: { Authorization: uploadData.authorizationToken, "Content-Type": "b2/x-auto", "X-Bz-File-Name": `issue_uploads/${image.name.replace(/\s/gm, "-")}`, "X-Bz-Content-Sha1": "do_not_verify", }, }).then((res) => res.json())) as any; const { fileName } = result; const url = `https://files.thoriumsim.com/file/thorium-public/${fileName}`; yield url; // returns true meaning that the save was successful return true; }} /> e.target.value !== "Select One" && setInvalid(invalid.filter((i) => i !== "type")) } name="label" > Select One {availableLabels.map((label) => ( {label} ))} Submit )} ); }
To better track issues and collect feedback, you must be logged in to a Thorium Nova account.
To submit issues, you must connect your Thorium Nova account to Github.