Skip to content

Commit

Permalink
chore: fix reactivity
Browse files Browse the repository at this point in the history
  • Loading branch information
bacali95 committed Nov 2, 2024
1 parent fd50013 commit 7be343e
Show file tree
Hide file tree
Showing 42 changed files with 352 additions and 80 deletions.
8 changes: 7 additions & 1 deletion .prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
module.exports = {
printWidth: 100,
singleQuote: true,
importOrder: ['^ace-builds/(.*)$', '^@prisma/(.*)$', '^@core/(.*)$', '^[./]'],
importOrder: [
'^ace-builds/(.*)$',
'^@prisma/(.*)$',
'^@tun-judge/(.*)$',
'^@core/(.*)$',
'^[./]',
],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/core/components/ProblemSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import { Prisma, ScoreCache, Submission } from '@prisma/client';
import { useActiveContest, useAuthContext } from '@core/contexts';
import { contestStartedAndNotOver, dateComparator, formatBytes } from '@core/utils';

import Balloon from '../../../public/assets/balloon.svg?react';
import { NoActiveContest } from './NoActiveContest';
import { PageTemplate } from './PageTemplate';
import { SubmitForm } from './SubmitForm';
import Balloon from './balloon.svg?react';

type ContestProblem = Prisma.ContestProblemGetPayload<{
include: { problem: true };
Expand Down
5 changes: 4 additions & 1 deletion apps/client/src/core/components/Scoreboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useParams } from 'react-router-dom';
import { Flex, Tooltip, cn } from 'tw-react-components';

import { useActiveContest, useAuthContext } from '@core/contexts';
import { useOnWebSocketEvent } from '@core/hooks';
import { formatRestTime, getRGBColorContrast, request } from '@core/utils';

import { NoActiveContest } from './NoActiveContest';
Expand Down Expand Up @@ -34,7 +35,7 @@ export const Scoreboard: FC<Props> = ({ className, compact }) => {
const { profile, isUserJury } = useAuthContext();
const { id } = useParams<{ id: string }>();

const { currentContest, activeContests } = useActiveContest();
const { currentContest, activeContests, refreshContests } = useActiveContest();

const contest = useMemo(
() => (id ? activeContests.find((c) => c.id === +id) : currentContest),
Expand All @@ -43,6 +44,8 @@ export const Scoreboard: FC<Props> = ({ className, compact }) => {

const [standing, setStanding] = useState<TeamStandingRow[]>([]);

useOnWebSocketEvent('scoreboard', refreshContests);

useEffect(() => {
if (contest?.scoreCaches) {
const cache: { [teamId: string]: TeamStandingRow } = {};
Expand Down
15 changes: 13 additions & 2 deletions apps/client/src/core/components/SubmitForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormDialog, FormInputs } from 'tw-react-components';
import { FileKind, Submission } from '@prisma/client';

import { useActiveContest, useAuthContext } from '@core/contexts';
import { useCreateSubmission, useFindManyLanguage } from '@core/queries';
import { useCountSubmission, useCreateSubmission, useFindManyLanguage } from '@core/queries';
import { uploadFile } from '@core/utils';

type Props = {
Expand All @@ -21,6 +21,17 @@ export const SubmitForm: FC<Props> = ({ submission, onClose }) => {

const form = useForm<Submission>({ defaultValues: structuredClone(submission) });

const { data: submissionsCount } = useCountSubmission(
{
where: {
contestId: currentContest?.id,
teamId: profile?.teamId ?? undefined,
problemId: form.watch('problemId'),
},
},
{ enabled: !!form.watch('problemId') },
);

const { data: languages = [] } = useFindManyLanguage();
const { mutateAsync } = useCreateSubmission();

Expand All @@ -33,7 +44,7 @@ export const SubmitForm: FC<Props> = ({ submission, onClose }) => {

if (sourceFile) {
const source = await uploadFile(sourceFile, {
name: `Submissions/${profile.team.name}/${sourceFile.name}`,
name: `Submissions/${profile.team.name}/p-${submission.problemId}-n-${submissionsCount}-${sourceFile.name}`,
type: sourceFile.type,
size: sourceFile.size,
md5Sum: '',
Expand Down
File renamed without changes
10 changes: 8 additions & 2 deletions apps/client/src/core/contexts/active-contest/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ActiveContest = {
activeContests: Contest[];
currentContest?: Contest;
setCurrentContest: (contest?: Contest) => void;
refreshContests: () => void;
};

export const ActiveContestContext = createContext<ActiveContest | undefined>(undefined);
Expand All @@ -23,7 +24,7 @@ export const ActiveContestProvider: FC<PropsWithChildren> = ({ children }) => {
const [now, setNow] = useState(new Date());
const [currentContest, setCurrentContest] = useState<Contest>();

const { data: contests = [] } = useFindManyContest({
const { data: contests = [], refetch } = useFindManyContest({
where: {
enabled: true,
activateTime: { lte: now },
Expand Down Expand Up @@ -51,7 +52,12 @@ export const ActiveContestProvider: FC<PropsWithChildren> = ({ children }) => {

return (
<ActiveContestContext.Provider
value={{ activeContests: contests, currentContest, setCurrentContest }}
value={{
activeContests: contests,
currentContest,
setCurrentContest,
refreshContests: refetch,
}}
>
{children}
</ActiveContestContext.Provider>
Expand Down
1 change: 1 addition & 0 deletions apps/client/src/core/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './useDownloadedFile';
export * from './useOnWebSocketEvent';
export * from './usePagination';
export * from './useSorting';
export * from './useTimeLeftToContest';
23 changes: 23 additions & 0 deletions apps/client/src/core/hooks/useOnWebSocketEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect } from 'react';

import { WebSocketEvent } from '@tun-judge/shared';

import { useWebSocketContext } from '@core/contexts';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function useOnWebSocketEvent<T extends (...args: any[]) => any>(
event: WebSocketEvent,
callback: T,
) {
const { socket } = useWebSocketContext();

useEffect(() => {
if (!socket) return;

socket.on(event, callback);

return () => {
socket.off(event, callback);
};
}, [socket, event, callback]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FC } from 'react';
import { Link, useParams } from 'react-router-dom';
import { Button, Sidebar } from 'tw-react-components';

import { useOnWebSocketEvent } from '@core/hooks';
import { useFindFirstContest, useFindManyClarification } from '@core/queries';

import { ClarificationGroupTab } from './ClarificationGroupTab';
Expand All @@ -15,7 +16,7 @@ export const ClarificationsSidebar: FC = () => {
include: { problems: { include: { problem: true } } },
});

const { data: clarifications = [] } = useFindManyClarification(
const { data: clarifications = [], refetch } = useFindManyClarification(
{
where: { contestId: parseInt(contestId ?? '-1') },
include: {
Expand All @@ -27,6 +28,8 @@ export const ClarificationsSidebar: FC = () => {
{ enabled: !!contestId },
);

useOnWebSocketEvent('clarifications', refetch);

return (
<Sidebar className="[--sidebar-width:250px]" collapsible="none">
<Sidebar.Header>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import Ansi from 'ansi-to-react';
import { FC, useEffect, useState } from 'react';
import { Sheet } from 'tw-react-components';
import { FC, useCallback, useState } from 'react';
import { Button, Flex, Sheet } from 'tw-react-components';

import { useWebSocketContext } from '@core/contexts';
import { useOnWebSocketEvent } from '@core/hooks';

type JudgeHostLogsViewerProps = {
hostname?: string;
onClose: () => void;
};

export const JudgeHostLogsViewer: FC<JudgeHostLogsViewerProps> = ({ hostname, onClose }) => {
const { socket } = useWebSocketContext();

const [logs, setLogs] = useState<string[]>([]);

useEffect(() => {
if (!hostname) return;

const event = `judgeHost-${hostname}-logs`;

socket.on(event, (logLine: string) => {
setLogs((logs) => [...logs, logLine]);
const terminalSegment = document.getElementById('terminal-logs');
terminalSegment && (terminalSegment.scrollTop = terminalSegment.scrollHeight);
});
const updateLogs = useCallback((logLine: string) => {
setLogs((logs) => [...logs, logLine]);
const terminalSegment = document.getElementById('terminal-logs');
terminalSegment && (terminalSegment.scrollTop = terminalSegment.scrollHeight);
}, []);

return () => {
socket.off(event);
};
}, [hostname, socket]);
useOnWebSocketEvent(`judgeHost-${hostname}-logs`, updateLogs);

return (
<Sheet open={!!hostname} onOpenChange={(value) => !value && onClose()}>
<Sheet.Content className="!max-w-7xl">
<Sheet.Header>
<Sheet.Title>Judge Host '{hostname}' logs</Sheet.Title>
</Sheet.Header>
<div id="terminal-logs" className="h-96 overflow-auto rounded-md bg-slate-900 text-white">
<Flex
id="terminal-logs"
className="gap-0 overflow-auto rounded-md bg-black p-2 text-white"
direction="column"
fullHeight
fullWidth
>
{logs.map((log, index) => (
<span key={index}>
<Ansi>{log}</Ansi>
<br />
</span>
<Ansi key={index}>{log}</Ansi>
))}
</div>
</Flex>
<Sheet.Footer>
<Sheet.Close asChild>
<Button color="red" onClick={onClose}>
Close
</Button>
</Sheet.Close>
</Sheet.Footer>
</Sheet.Content>
</Sheet>
);
Expand Down
22 changes: 14 additions & 8 deletions apps/client/src/pages/admin/views/submissions/SubmissionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import { Prisma, User } from '@prisma/client';
import { CodeEditor, PageTemplate } from '@core/components';
import { LANGUAGES_MAP } from '@core/constants';
import { useAuthContext } from '@core/contexts';
import { useDownloadedFile } from '@core/hooks';
import { useDownloadedFile, useOnWebSocketEvent } from '@core/hooks';
import { useFindFirstSubmission, useUpdateJudging, useUpdateSubmission } from '@core/queries';
import { request } from '@core/utils';

import { SubmissionViewDetails } from './SubmissionViewDetails';
import { SubmissionsViewJudgingRuns } from './SubmissionViewJudgingRuns';
Expand Down Expand Up @@ -47,7 +48,7 @@ export type Testcase = JudgingRun['testcase'];

export const SubmissionsView: FC = () => {
const { contestId } = useParams();
const { profile } = useAuthContext();
const { profile, isUserAdmin } = useAuthContext();
const navigate = useNavigate();
const { submissionId } = useParams();

Expand Down Expand Up @@ -76,6 +77,8 @@ export const SubmissionsView: FC = () => {
const { mutateAsync: updateJudging } = useUpdateJudging();
const { mutateAsync: updateSubmission } = useUpdateSubmission();

useOnWebSocketEvent('judgings', refetch);

const content = useDownloadedFile(submission?.sourceFileName);
const latestJudging = useMemo(() => {
const validJudgings = submission?.judgings.filter((j) => j.valid);
Expand All @@ -87,20 +90,23 @@ export const SubmissionsView: FC = () => {
);
}, [submission?.judgings]);

const toggleSubmissionValid = () => {
const toggleSubmissionValid = async () => {
if (!submission) return;

updateSubmission({ where: { id: submission.id }, data: { valid: !submission.valid } });
await updateSubmission({ where: { id: submission.id }, data: { valid: !submission.valid } });
await request('api/scoreboard/refresh-score-cache', 'PATCH');
};

const rejudge = async () => {
if (!submission) return;

await updateSubmission({ where: { id: submission.id }, data: { judgeHostId: null } });
await request('api/scoreboard/refresh-score-cache', 'PATCH');
};

const markVerified = (judgingId: number) => {
updateJudging({ where: { id: judgingId }, data: { verified: true } });
const markVerified = async (judgingId: number) => {
await updateJudging({ where: { id: judgingId }, data: { verified: true } });
await request('api/scoreboard/refresh-score-cache', 'PATCH');

navigate(`/contests/${contestId}/submissions`);
};
Expand Down Expand Up @@ -137,7 +143,7 @@ export const SubmissionsView: FC = () => {
title={`Submission ${submission.id}`}
actions={
<>
{isSubmissionClaimedByMe(latestJudging, profile) && (
{(isSubmissionClaimedByMe(latestJudging, profile) || isUserAdmin) && (
<Button
color="gray"
prefixIcon={submission.valid ? ClipboardXIcon : ClipboardPlusIcon}
Expand All @@ -148,7 +154,7 @@ export const SubmissionsView: FC = () => {
)}
{latestJudging?.result &&
latestJudging.verified &&
isSubmissionClaimedByMe(latestJudging, profile) && (
(isSubmissionClaimedByMe(latestJudging, profile) || isUserAdmin) && (
<Button color="red" onClick={rejudge}>
Rejudge
</Button>
Expand Down
12 changes: 10 additions & 2 deletions apps/client/src/pages/admin/views/submissions/SubmissionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Prisma, Testcase } from '@prisma/client';

import { SubmissionResult } from '@core/components';
import { useAuthContext } from '@core/contexts';
import { usePagination } from '@core/hooks';
import { useOnWebSocketEvent, usePagination } from '@core/hooks';
import {
useCountSubmission,
useFindFirstContest,
Expand Down Expand Up @@ -36,7 +36,11 @@ export const SubmissionsList: FC = () => {
const { data: totalItems = 0 } = useCountSubmission({
where: { contestId: parseInt(contestId ?? '-1') },
});
const { data: submissions = [], isLoading } = useFindManySubmission({
const {
data: submissions = [],
isLoading,
refetch,
} = useFindManySubmission({
where: { contestId: parseInt(contestId ?? '-1') },
include: {
team: true,
Expand All @@ -54,6 +58,10 @@ export const SubmissionsList: FC = () => {
});
const { mutateAsync: updateJudging } = useUpdateJudging();

useOnWebSocketEvent('judgings', refetch);
useOnWebSocketEvent('judgingRuns', refetch);
useOnWebSocketEvent('submissions', refetch);

const claimSubmission = async (judgingId: number) => {
if (!profile) return;

Expand Down
5 changes: 4 additions & 1 deletion apps/client/src/pages/team/views/ClarificationsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button, DataTable, DataTableColumn, Flex } from 'tw-react-components';

import { ChatBoxDialog, Clarification, PageTemplate } from '@core/components';
import { useActiveContest, useAuthContext } from '@core/contexts';
import { useOnWebSocketEvent } from '@core/hooks';
import { useFindManyClarification } from '@core/queries';
import { countUnseenMessages } from '@core/utils';

Expand All @@ -13,7 +14,7 @@ export const ClarificationsList: FC<{ className?: string }> = ({ className }) =>

const [clarificationId, setClarificationId] = useState<number>();

const { data: clarifications = [] } = useFindManyClarification(
const { data: clarifications = [], refetch } = useFindManyClarification(
{
include: {
team: true,
Expand All @@ -25,6 +26,8 @@ export const ClarificationsList: FC<{ className?: string }> = ({ className }) =>
{ refetchInterval: import.meta.env.MODE !== 'development' && 5000 },
);

useOnWebSocketEvent('clarifications', refetch);

const columns: DataTableColumn<Clarification>[] = [
{
header: '#',
Expand Down
Loading

0 comments on commit 7be343e

Please sign in to comment.