Skip to content

Commit 92b0275

Browse files
authored
Reduce unnecessary data refetching (#357)
- Reconnection logic now refreshes only when relevant channels are restored. - Submissions page only loads history when the modal is opened
1 parent 3875a24 commit 92b0275

File tree

3 files changed

+475
-178
lines changed

3 files changed

+475
-178
lines changed

app/course/[course_id]/assignments/[assignment_id]/submissions/[submissions_id]/layout.tsx

Lines changed: 153 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,11 @@ function SubmissionReviewScoreTweak() {
241241
</Box>
242242
);
243243
}
244-
function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGraderResultsOutputTestsAndRubric }) {
245-
const pathname = usePathname();
246-
const invalidate = useInvalidate();
247-
const router = useRouter();
248-
const [hasNewSubmission, setHasNewSubmission] = useState<boolean>(false);
244+
function SubmissionHistoryContents({
245+
submission
246+
}: {
247+
submission: SubmissionWithFilesGraderResultsOutputTestsAndRubric;
248+
}) {
249249
const groupOrProfileFilter: CrudFilter = submission.assignment_group_id
250250
? {
251251
field: "assignment_group_id",
@@ -257,6 +257,7 @@ function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGrad
257257
operator: "eq",
258258
value: submission.profile_id
259259
};
260+
const invalidate = useInvalidate();
260261
const { data, isLoading } = useList<SubmissionWithGraderResultsAndReview>({
261262
resource: "submissions",
262263
meta: {
@@ -280,10 +281,152 @@ function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGrad
280281
pageSize: 1000
281282
}
282283
});
284+
const router = useRouter();
285+
const { time_zone } = useCourse();
286+
const [isActivating, setIsActivating] = useState(false);
287+
const pathname = usePathname();
288+
const isGraderInterface = pathname.includes("/grade");
289+
const { dueDate } = useAssignmentDueDate(submission.assignments, {
290+
studentPrivateProfileId: submission.profile_id || undefined,
291+
assignmentGroupId: submission.assignment_group_id || undefined
292+
});
293+
const isStaff = useIsGraderOrInstructor();
294+
const now = TZDate.tz(time_zone ?? "America/New_York").getTime();
295+
const disableActivationButton = Boolean(dueDate && now > dueDate.getTime() && !isStaff);
296+
if (isLoading) {
297+
return <Skeleton height="100px" />;
298+
}
299+
return (
300+
<>
301+
<Text>Submission History</Text>
302+
<Box
303+
maxHeight="400px"
304+
overflowY="auto"
305+
css={{
306+
"&::-webkit-scrollbar": {
307+
width: "8px"
308+
},
309+
"&::-webkit-scrollbar-track": {
310+
background: "#f1f1f1",
311+
borderRadius: "4px"
312+
},
313+
"&::-webkit-scrollbar-thumb": {
314+
background: "#888",
315+
borderRadius: "4px"
316+
},
317+
"&::-webkit-scrollbar-thumb:hover": {
318+
background: "#555"
319+
}
320+
}}
321+
>
322+
<Toaster />
323+
<Table.Root>
324+
<Table.Header>
325+
<Table.Row>
326+
<Table.ColumnHeader>#</Table.ColumnHeader>
327+
<Table.ColumnHeader>Date</Table.ColumnHeader>
328+
<Table.ColumnHeader>Auto Grader Score</Table.ColumnHeader>
329+
<Table.ColumnHeader>Total Score</Table.ColumnHeader>
330+
<Table.ColumnHeader>Actions</Table.ColumnHeader>
331+
</Table.Row>
332+
</Table.Header>
333+
<Table.Body>
334+
{data?.data.map((historical_submission) => {
335+
const link = isGraderInterface
336+
? `/course/${historical_submission.class_id}/grade/assignments/${historical_submission.assignment_id}/submissions/${historical_submission.id}`
337+
: `/course/${historical_submission.class_id}/assignments/${historical_submission.assignment_id}/submissions/${historical_submission.id}`;
338+
return (
339+
<Table.Row key={historical_submission.id} bg={pathname.startsWith(link) ? "bg.emphasized" : undefined}>
340+
<Table.Cell>
341+
<Link href={link}>
342+
{historical_submission.is_active && <ActiveSubmissionIcon />}
343+
{historical_submission.ordinal}
344+
</Link>
345+
</Table.Cell>
346+
<Table.Cell>
347+
<Link href={link}>
348+
{formatRelative(
349+
new TZDate(
350+
historical_submission.created_at || new Date().toUTCString(),
351+
time_zone || "America/New_York"
352+
),
353+
TZDate.tz(time_zone || "America/New_York")
354+
)}
355+
</Link>
356+
</Table.Cell>
357+
<Table.Cell>
358+
<Link href={link}>
359+
{!historical_submission.grader_results
360+
? "In Progress"
361+
: historical_submission.grader_results && historical_submission.grader_results.errors
362+
? "Error"
363+
: `${historical_submission.grader_results?.score}/${historical_submission.grader_results?.max_score}`}
364+
</Link>
365+
</Table.Cell>
366+
<Table.Cell>
367+
<Link href={link}>
368+
{historical_submission.submission_reviews?.completed_at &&
369+
historical_submission.submission_reviews?.total_score +
370+
"/" +
371+
historical_submission.assignments.total_points}
372+
</Link>
373+
</Table.Cell>
374+
<Table.Cell>
375+
{historical_submission.is_active ? (
376+
<>This submission is active</>
377+
) : historical_submission.is_not_graded ? (
378+
<>Not for grading</>
379+
) : (
380+
<Button
381+
variant="outline"
382+
size="xs"
383+
disabled={disableActivationButton}
384+
loading={isActivating}
385+
onClick={async () => {
386+
setIsActivating(true);
387+
try {
388+
const supabase = createClient();
389+
await activateSubmission({ submission_id: historical_submission.id }, supabase);
390+
invalidate({ resource: "submissions", invalidates: ["list"] });
391+
toaster.create({
392+
title: "Active submission changed",
393+
type: "success"
394+
});
395+
router.push(link);
396+
} catch (error) {
397+
const errorId = Sentry.captureException(error);
398+
toaster.create({
399+
title: "Error activating submission",
400+
description: `We have recorded this error with trace ID: ${errorId}`,
401+
type: "error"
402+
});
403+
} finally {
404+
setIsActivating(false);
405+
}
406+
}}
407+
>
408+
<Icon as={FaCheckCircle} />
409+
Activate
410+
</Button>
411+
)}
412+
</Table.Cell>
413+
</Table.Row>
414+
);
415+
})}
416+
</Table.Body>
417+
</Table.Root>
418+
</Box>
419+
</>
420+
);
421+
}
422+
function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGraderResultsOutputTestsAndRubric }) {
423+
const invalidate = useInvalidate();
424+
const [hasNewSubmission, setHasNewSubmission] = useState<boolean>(false);
425+
283426
useList<Submission>({
284427
resource: "submissions",
285428
meta: {
286-
select: "*"
429+
select: "id, assignment_group_id, profile_id, is_active"
287430
},
288431
filters: [
289432
{
@@ -306,25 +449,12 @@ function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGrad
306449
invalidate({ resource: "submissions", invalidates: ["list"] });
307450
}
308451
});
309-
const { time_zone } = useCourse();
310-
const [isActivating, setIsActivating] = useState(false);
311-
const isGraderInterface = pathname.includes("/grade");
312-
const { dueDate } = useAssignmentDueDate(submission.assignments, {
313-
studentPrivateProfileId: submission.profile_id || undefined,
314-
assignmentGroupId: submission.assignment_group_id || undefined
315-
});
316-
const isStaff = useIsGraderOrInstructor();
317-
const disableActivationButton = Boolean(
318-
dueDate &&
319-
TZDate.tz(time_zone ?? "America/New_York").getTime() >
320-
new TZDate(dueDate, time_zone ?? "America/New_York").getTime() &&
321-
!isStaff
322-
);
323-
if (isLoading || !submission.assignments) {
452+
453+
if (!submission.assignments) {
324454
return <Skeleton height="20px" />;
325455
}
326456
return (
327-
<PopoverRoot>
457+
<PopoverRoot lazyMount unmountOnExit>
328458
<PopoverTrigger asChild>
329459
<Button variant={hasNewSubmission ? "solid" : "outline"} colorPalette={hasNewSubmission ? "yellow" : "default"}>
330460
<Icon as={FaHistory} />
@@ -335,127 +465,7 @@ function SubmissionHistory({ submission }: { submission: SubmissionWithFilesGrad
335465
<PopoverContent minWidth={{ base: "none", md: "lg" }}>
336466
<PopoverArrow />
337467
<PopoverBody>
338-
<Text>Submission History</Text>
339-
<Box
340-
maxHeight="400px"
341-
overflowY="auto"
342-
css={{
343-
"&::-webkit-scrollbar": {
344-
width: "8px"
345-
},
346-
"&::-webkit-scrollbar-track": {
347-
background: "#f1f1f1",
348-
borderRadius: "4px"
349-
},
350-
"&::-webkit-scrollbar-thumb": {
351-
background: "#888",
352-
borderRadius: "4px"
353-
},
354-
"&::-webkit-scrollbar-thumb:hover": {
355-
background: "#555"
356-
}
357-
}}
358-
>
359-
<Toaster />
360-
<Table.Root>
361-
<Table.Header>
362-
<Table.Row>
363-
<Table.ColumnHeader>#</Table.ColumnHeader>
364-
<Table.ColumnHeader>Date</Table.ColumnHeader>
365-
<Table.ColumnHeader>Auto Grader Score</Table.ColumnHeader>
366-
<Table.ColumnHeader>Total Score</Table.ColumnHeader>
367-
<Table.ColumnHeader>Actions</Table.ColumnHeader>
368-
</Table.Row>
369-
</Table.Header>
370-
<Table.Body>
371-
{data?.data.map((historical_submission) => {
372-
const link = isGraderInterface
373-
? `/course/${historical_submission.class_id}/grade/assignments/${historical_submission.assignment_id}/submissions/${historical_submission.id}`
374-
: `/course/${historical_submission.class_id}/assignments/${historical_submission.assignment_id}/submissions/${historical_submission.id}`;
375-
return (
376-
<Table.Row
377-
key={historical_submission.id}
378-
bg={pathname.startsWith(link) ? "bg.emphasized" : undefined}
379-
>
380-
<Table.Cell>
381-
<Link href={link}>
382-
{historical_submission.is_active && <ActiveSubmissionIcon />}
383-
{historical_submission.ordinal}
384-
</Link>
385-
</Table.Cell>
386-
<Table.Cell>
387-
<Link href={link}>
388-
{formatRelative(
389-
new TZDate(
390-
historical_submission.created_at || new Date().toUTCString(),
391-
time_zone || "America/New_York"
392-
),
393-
TZDate.tz(time_zone || "America/New_York")
394-
)}
395-
</Link>
396-
</Table.Cell>
397-
<Table.Cell>
398-
<Link href={link}>
399-
{!historical_submission.grader_results
400-
? "In Progress"
401-
: historical_submission.grader_results && historical_submission.grader_results.errors
402-
? "Error"
403-
: `${historical_submission.grader_results?.score}/${historical_submission.grader_results?.max_score}`}
404-
</Link>
405-
</Table.Cell>
406-
<Table.Cell>
407-
<Link href={link}>
408-
{historical_submission.submission_reviews?.completed_at &&
409-
historical_submission.submission_reviews?.total_score +
410-
"/" +
411-
historical_submission.assignments.total_points}
412-
</Link>
413-
</Table.Cell>
414-
<Table.Cell>
415-
{historical_submission.is_active ? (
416-
<>This submission is active</>
417-
) : historical_submission.is_not_graded ? (
418-
<>Not for grading</>
419-
) : (
420-
<Button
421-
variant="outline"
422-
size="xs"
423-
disabled={disableActivationButton}
424-
loading={isActivating}
425-
onClick={async () => {
426-
setIsActivating(true);
427-
try {
428-
const supabase = createClient();
429-
await activateSubmission({ submission_id: historical_submission.id }, supabase);
430-
invalidate({ resource: "submissions", invalidates: ["list"] });
431-
toaster.create({
432-
title: "Active submission changed",
433-
type: "success"
434-
});
435-
router.push(link);
436-
} catch (error) {
437-
const errorId = Sentry.captureException(error);
438-
toaster.create({
439-
title: "Error activating submission",
440-
description: `We have recorded this error with trace ID: ${errorId}`,
441-
type: "error"
442-
});
443-
} finally {
444-
setIsActivating(false);
445-
}
446-
}}
447-
>
448-
<Icon as={FaCheckCircle} />
449-
Activate
450-
</Button>
451-
)}
452-
</Table.Cell>
453-
</Table.Row>
454-
);
455-
})}
456-
</Table.Body>
457-
</Table.Root>
458-
</Box>
468+
<SubmissionHistoryContents submission={submission} />
459469
</PopoverBody>
460470
</PopoverContent>
461471
</PopoverRoot>

0 commit comments

Comments
 (0)