Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
car-crushers-portal/components/NewGameBan.tsx
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
273 lines (244 sloc)
6.6 KB
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
import { | |
Button, | |
Input, | |
Link, | |
Modal, | |
ModalBody, | |
ModalCloseButton, | |
ModalContent, | |
ModalFooter, | |
ModalHeader, | |
ModalOverlay, | |
Radio, | |
RadioGroup, | |
Table, | |
TableContainer, | |
Tbody, | |
Text, | |
Td, | |
Th, | |
Thead, | |
Tr, | |
VStack, | |
useToast, | |
} from "@chakra-ui/react"; | |
import { useState } from "react"; | |
export default function (props: { isOpen: boolean; onClose: () => void }) { | |
const actionMap: { [k: string]: number } = {}; | |
const [users, setUsers] = useState([] as string[]); | |
const toast = useToast(); | |
const fileTypes: { [k: string]: string } = { | |
gif: "image/gif", | |
heic: "image/heic", | |
heif: "image/heif", | |
jfif: "image/jpeg", | |
jpeg: "image/jpeg", | |
jpg: "image/jpg", | |
m4v: "video/x-m4v", | |
mkv: "video/x-matroska", | |
mov: "video/quicktime", | |
mp4: "video/mp4", | |
png: "image/png", | |
webp: "image/webp", | |
webm: "video/webm", | |
wmv: "video/x-ms-wmv", | |
}; | |
function addUser(user: string) { | |
const newUsers = [...users]; | |
newUsers.push(user); | |
setUsers(newUsers); | |
} | |
function removeUser(user: string) { | |
const newUsers = [...users]; | |
const userIdx = newUsers.indexOf(user); | |
if (userIdx === -1) return; | |
newUsers.splice(userIdx, 1); | |
setUsers(newUsers); | |
delete actionMap[user]; | |
} | |
function reset() { | |
(document.getElementById("username") as HTMLInputElement).value = ""; | |
(document.getElementById("evidence") as HTMLInputElement).value = ""; | |
setUsers([]); | |
Object.keys(actionMap).forEach((k) => delete actionMap[k]); | |
props.onClose(); | |
} | |
async function submit() { | |
const actions: number[] = []; | |
const usernames: string[] = []; | |
for (const [u, a] of Object.entries(actionMap)) { | |
actions.push(a); | |
usernames.push(u); | |
} | |
if (!usernames.length || !actions.length) return; | |
const files = (document.getElementById("evidence") as HTMLInputElement) | |
.files; | |
if (!files) return; | |
const [evidence] = files; | |
const submitReq = await fetch("/api/reports/submit", { | |
body: JSON.stringify({ | |
actions, | |
bypass: true, | |
filename: evidence.name, | |
filesize: evidence.size, | |
usernames, | |
}), | |
headers: { | |
"content-type": "application/json", | |
}, | |
method: "POST", | |
}); | |
if (!submitReq.ok) { | |
toast({ | |
description: ((await submitReq.json()) as { error: string }).error, | |
status: "error", | |
title: "Failed to submit report", | |
}); | |
return; | |
} | |
const { id, upload_url }: { [k: string]: string } = await submitReq.json(); | |
const fileUpload = await fetch(upload_url, { | |
body: evidence, | |
headers: { | |
"content-type": | |
evidence.type || | |
fileTypes[ | |
evidence.name.split(".")[evidence.name.split(".").length - 1] | |
], | |
}, | |
method: "PUT", | |
}); | |
if (!fileUpload.ok) { | |
await fetch("/api/reports/recall", { | |
body: JSON.stringify({ id }), | |
headers: { | |
"content-type": "application/json", | |
}, | |
method: "POST", | |
}); | |
toast({ | |
description: "Failed to upload file", | |
status: "error", | |
title: "Error", | |
}); | |
return; | |
} | |
await fetch("/api/reports/complete", { | |
body: JSON.stringify({ id }), | |
headers: { | |
"content-type": "application/json", | |
}, | |
method: "POST", | |
}); | |
toast({ | |
description: "User moderated", | |
status: "success", | |
title: "Success", | |
}); | |
} | |
return ( | |
<Modal isCentered isOpen={props.isOpen} onClose={props.onClose}> | |
<ModalOverlay /> | |
<ModalContent> | |
<ModalHeader>New Game Ban</ModalHeader> | |
<ModalCloseButton /> | |
<ModalBody> | |
<Text>Username(s)</Text> | |
<Input id="username" mb="8px" placeholder="builderman" /> | |
<Button | |
onClick={function () { | |
const user = ( | |
document.getElementById("username") as HTMLInputElement | |
).value; | |
if ( | |
!user || | |
user.length < 3 || | |
user.length > 20 || | |
// @ts-expect-error | |
user.match(/_/g)?.length > 1 || | |
user.match(/\W/) | |
) { | |
toast({ | |
description: "Check the username and try again", | |
duration: 5000, | |
isClosable: true, | |
status: "error", | |
title: "Invalid Username", | |
}); | |
return; | |
} | |
addUser(user); | |
}} | |
> | |
Add | |
</Button> | |
<br /> | |
<br /> | |
<TableContainer> | |
<Table variant="simple"> | |
<Thead> | |
<Tr> | |
<Th>Username</Th> | |
<Th>Punishment</Th> | |
<Th>Remove</Th> | |
</Tr> | |
</Thead> | |
<Tbody> | |
{users.map((user) => ( | |
<Tr key={user}> | |
<Td>{user}</Td> | |
<Td> | |
<RadioGroup | |
onChange={(val) => | |
Object.defineProperty(actionMap, user, { | |
value: parseInt(val), | |
}) | |
} | |
> | |
<VStack> | |
<Radio value="0">Do Nothing</Radio> | |
<Radio value="1">Hide from Leaderboards</Radio> | |
<Radio value="2">Ban</Radio> | |
</VStack> | |
</RadioGroup> | |
</Td> | |
<Td> | |
<Link onClick={() => removeUser(user)}>Remove</Link> | |
</Td> | |
</Tr> | |
))} | |
</Tbody> | |
</Table> | |
</TableContainer> | |
<br /> | |
<br /> | |
<Text>Evidence</Text> | |
<Button | |
mr="8px" | |
onClick={() => document.getElementById("evidence")?.click()} | |
> | |
Select Files | |
</Button> | |
<input id="evidence" type="file" /> | |
</ModalBody> | |
</ModalContent> | |
<ModalFooter> | |
<Button onClick={reset}>Cancel</Button> | |
<Button | |
colorScheme="blue" | |
disabled={ | |
!( | |
Object.entries(actionMap).length && | |
(document.getElementById("evidence") as HTMLInputElement).files | |
?.length | |
) | |
} | |
ml="8px" | |
onClick={async () => await submit()} | |
> | |
Submit | |
</Button> | |
</ModalFooter> | |
</Modal> | |
); | |
} |