Use MessageChannels to self-delete queue items

Previous method would cause site to die
This commit is contained in:
Regalijan 2023-10-24 00:32:53 -04:00
parent 912cd0581a
commit 5d38bae59c
Signed by: regalijan
GPG Key ID: 5D4196DA269EF520
5 changed files with 75 additions and 36 deletions

View File

@ -18,7 +18,7 @@ import {
useToast, useToast,
VStack, VStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { type ReactElement, useEffect, useState } from "react"; import { type ReactNode, useEffect, useState } from "react";
import AppealCard from "../../components/AppealCard.js"; import AppealCard from "../../components/AppealCard.js";
import GameAppealCard from "../../components/GameAppealCard.js"; import GameAppealCard from "../../components/GameAppealCard.js";
import NewGameBan from "../../components/NewGameBan.js"; import NewGameBan from "../../components/NewGameBan.js";
@ -115,8 +115,11 @@ export default function () {
const pageProps = useLoaderData<typeof loader>(); const pageProps = useLoaderData<typeof loader>();
const isDesktop = useBreakpointValue({ base: false, lg: true }); const isDesktop = useBreakpointValue({ base: false, lg: true });
const entryTypes = []; const entryTypes = [];
const [entries, setEntries] = useState([] as ReactElement[]); const [entries, setEntries] = useState(
[] as { element: ReactNode; id: string }[],
);
const [before, setBefore] = useState(Date.now()); const [before, setBefore] = useState(Date.now());
const [messageChannel] = useState(null as MessageChannel | null);
const toast = useToast(); const toast = useToast();
for (const type of pageProps.entry_types) for (const type of pageProps.entry_types)
@ -126,6 +129,16 @@ export default function () {
</option>, </option>,
); );
useEffect(() => {
if (!messageChannel) return;
messageChannel.port1.onmessage = function (ev) {
const { data }: { data: string } = ev;
setEntries([...entries].filter((entry) => entry.id !== data));
};
}, [messageChannel]);
async function updateQueue( async function updateQueue(
queue_type: string, queue_type: string,
before: number, before: number,
@ -212,24 +225,54 @@ export default function () {
switch (cardType) { switch (cardType) {
case "appeal": case "appeal":
newEntries.push(<AppealCard {...(entry as AppealCardProps)} />); newEntries.push({
element: (
<AppealCard
{...(entry as AppealCardProps & { port?: MessagePort })}
port={messageChannel?.port2}
/>
),
id: `appeal_${entry.id}`,
});
break; break;
case "gma": case "gma":
newEntries.push(<GameAppealCard {...(entry as GameAppealProps)} />); newEntries.push({
element: (
<GameAppealCard
{...(entry as GameAppealProps & { port?: MessagePort })}
port={messageChannel?.port2}
/>
),
id: `gma_${entry.id}`,
});
break; break;
case "inactivity": case "inactivity":
newEntries.push( newEntries.push({
<InactivityNoticeCard {...(entry as InactivityNoticeProps)} />, element: (
); <InactivityNoticeCard
{...(entry as InactivityNoticeProps & { port?: MessagePort })}
port={messageChannel?.port2}
/>
),
id: `inactivity_${entry.id}`,
});
break; break;
case "report": case "report":
newEntries.push(<ReportCard {...(entry as ReportCardProps)} />); newEntries.push({
element: (
<ReportCard
{...(entry as ReportCardProps & { port?: MessagePort })}
port={messageChannel?.port2}
/>
),
id: `report_${entry.id}`,
});
break; break;
} }
@ -311,7 +354,7 @@ export default function () {
{ItemDisplay} {ItemDisplay}
</Box> </Box>
{entries.length ? ( {entries.length ? (
entries entries.map((entry) => entry.element)
) : ( ) : (
<Container <Container
left="50%" left="50%"

View File

@ -21,7 +21,7 @@ import {
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export default function(props: AppealCardProps) { export default function(props: AppealCardProps & { port?: MessagePort }) {
const [dateString, setDateString] = useState( const [dateString, setDateString] = useState(
new Date(props.created_at).toUTCString() new Date(props.created_at).toUTCString()
); );
@ -80,7 +80,7 @@ export default function(props: AppealCardProps) {
onClose(); onClose();
setLoading(false); setLoading(false);
document.getElementById(`appeal_${props.id}`)?.remove(); props.port?.postMessage(`appeal_${props.id}`);
} }
return ( return (

View File

@ -20,7 +20,7 @@ import {
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useState } from "react"; import { useState } from "react";
export default function (props: GameAppealProps) { export default function (props: GameAppealProps & { port?: MessagePort }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [percentage, setPercentage] = useState(0); const [percentage, setPercentage] = useState(0);
const toast = useToast(); const toast = useToast();
@ -59,9 +59,7 @@ export default function (props: GameAppealProps) {
); );
setLoading(false); setLoading(false);
document props.port?.postMessage(`gma_${props.id}`);
.getElementById(`gma_${props.roblox_id}${props.created_at}`)
?.remove();
} }
const { isOpen, onClose, onOpen } = useDisclosure(); const { isOpen, onClose, onOpen } = useDisclosure();

View File

@ -11,11 +11,11 @@ import {
StackDivider, StackDivider,
Text, Text,
UnorderedList, UnorderedList,
useToast useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useState } from "react"; import { useState } from "react";
export default function(props: InactivityNoticeProps) { export default function (props: InactivityNoticeProps & { port?: MessagePort }) {
const toast = useToast(); const toast = useToast();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -24,9 +24,9 @@ export default function(props: InactivityNoticeProps) {
const decisionReq = await fetch(`/api/inactivity/${props.id}`, { const decisionReq = await fetch(`/api/inactivity/${props.id}`, {
body: JSON.stringify({ accepted }), body: JSON.stringify({ accepted }),
headers: { headers: {
"content-type": "application/json" "content-type": "application/json",
}, },
method: "POST" method: "POST",
}); });
if (!decisionReq.ok) { if (!decisionReq.ok) {
@ -35,7 +35,7 @@ export default function(props: InactivityNoticeProps) {
description: ((await decisionReq.json()) as { error: string }).error, description: ((await decisionReq.json()) as { error: string }).error,
isClosable: true, isClosable: true,
status: "error", status: "error",
title: "Oops" title: "Oops",
}); });
return; return;
@ -45,24 +45,22 @@ export default function(props: InactivityNoticeProps) {
description: `Inactivity notice ${accepted ? "accepted" : "denied"}.`, description: `Inactivity notice ${accepted ? "accepted" : "denied"}.`,
isClosable: true, isClosable: true,
status: "success", status: "success",
title: "Success" title: "Success",
}); });
setLoading(false); setLoading(false);
location.reload(); props.port?.postMessage(`inactivity_${props.id}`);
} }
const Approved = () => ( const Approved = () => (
<svg fill="currentColor" height="16" viewBox="0 0 16 16" width="16"> <svg fill="currentColor" height="16" viewBox="0 0 16 16" width="16">
<path <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
</svg> </svg>
); );
const Denied = () => ( const Denied = () => (
<svg fill="currentColor" height="16" viewBox="0 0 16 16" width="16"> <svg fill="currentColor" height="16" viewBox="0 0 16 16" width="16">
<path <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
</svg> </svg>
); );

View File

@ -13,11 +13,11 @@ import {
Stack, Stack,
Text, Text,
useToast, useToast,
VStack VStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useState } from "react"; import { useState } from "react";
export default function(props: ReportCardProps) { export default function (props: ReportCardProps & { port?: MessagePort }) {
const [attachmentIdx, setAttachmentIdx] = useState(0); const [attachmentIdx, setAttachmentIdx] = useState(0);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const toast = useToast(); const toast = useToast();
@ -26,7 +26,7 @@ export default function(props: ReportCardProps) {
for (let i = 0; i < props.target_ids.length; i++) for (let i = 0; i < props.target_ids.length; i++)
Object.defineProperty(targetMap, props.target_ids[i], { Object.defineProperty(targetMap, props.target_ids[i], {
value: props.target_usernames[i] value: props.target_usernames[i],
}); });
async function submitActions() { async function submitActions() {
@ -34,9 +34,9 @@ export default function(props: ReportCardProps) {
const submitReq = await fetch(`/api/reports/${props.id}/action`, { const submitReq = await fetch(`/api/reports/${props.id}/action`, {
body: JSON.stringify(actionMap), body: JSON.stringify(actionMap),
headers: { headers: {
"content-type": "application/json" "content-type": "application/json",
}, },
method: "POST" method: "POST",
}); });
if (!submitReq.ok) { if (!submitReq.ok) {
@ -44,7 +44,7 @@ export default function(props: ReportCardProps) {
toast({ toast({
description: ((await submitReq.json()) as { error: string }).error, description: ((await submitReq.json()) as { error: string }).error,
status: "error", status: "error",
title: "S̸̯̜̈́o̴̳̅̾̏̽m̴͔͕̈́̋ē̴̙͓̯̍̃ț̸͖̘̀h̶̛̳̝̐i̵̋͘͜ņ̷̙̤͌g̴̭̻̓̈́ ̴̘͍̦̪̆w̸̡̏̑̊é̸̠̖̹̂͜n̴̖̳̤̕t̴͚̊̊̕ ̸̛͙̺̬̎́w̴͈͑̋͊r̷̢̛o̵̱̩̍͋ͅṇ̸̝̰̮́g̵̡̢̦͕͂" title: "S̸̯̜̈́o̴̳̅̾̏̽m̴͔͕̈́̋ē̴̙͓̯̍̃ț̸͖̘̀h̶̛̳̝̐i̵̋͘͜ņ̷̙̤͌g̴̭̻̓̈́ ̴̘͍̦̪̆w̸̡̏̑̊é̸̠̖̹̂͜n̴̖̳̤̕t̴͚̊̊̕ ̸̛͙̺̬̎́w̴͈͑̋͊r̷̢̛o̵̱̩̍͋ͅṇ̸̝̰̮́g̵̡̢̦͕͂",
}); });
return; return;
@ -53,10 +53,10 @@ export default function(props: ReportCardProps) {
toast({ toast({
description: "Actions were successfully applied", description: "Actions were successfully applied",
status: "success", status: "success",
title: "Success" title: "Success",
}); });
setLoading(false); setLoading(false);
location.reload(); props.port?.postMessage(`report_${props.id}`);
} }
return ( return (
@ -154,7 +154,7 @@ export default function(props: ReportCardProps) {
Ban Ban
</Radio> </Radio>
</VStack> </VStack>
</RadioGroup> </RadioGroup>,
); );
} }