feat: enable TS strict mode (#168)

* feat: enable TS strict mode

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: colors bug with default

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: text type errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors - add definite assignment assertion

* fix: strict TS errors - add definite assignment assertion

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix(ESLint): error on no-explicit-any

* fix: type annotations for any types

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors (and remove packages)

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* feat: enable React.StrictMode

* fix: strict TS errors (done!)

* fix: build error

* fix: replace no-explicit-any assertions

* refactor: cleanup

* refactor: more cleanup

* style: prettier

---------

Co-authored-by: Lukas Zenick <lukas@utexas.edu>
Co-authored-by: Razboy20 <razboy20@gmail.com>
This commit is contained in:
doprz
2024-03-21 13:19:40 -05:00
committed by GitHub
parent 0c76052478
commit efed1c0edb
61 changed files with 562 additions and 1309 deletions

View File

@@ -20,16 +20,20 @@ import TeamLinks from '../TeamLinks';
export default function Calendar(): JSX.Element {
const calendarRef = useRef<HTMLDivElement>(null);
const { courseCells, activeSchedule } = useFlattenedCourseSchedule();
const [course, setCourse] = useState<Course | null>((): Course | null => {
const urlParams = new URLSearchParams(window.location.search);
const uniqueIdRaw = urlParams.get('uniqueId');
if (uniqueIdRaw === null) return null;
const uniqueId = Number(uniqueIdRaw);
const course = activeSchedule.courses.find(course => course.uniqueId === uniqueId);
if (course === undefined) return null;
urlParams.delete('uniqueId');
const newUrl = `${window.location.pathname}?${urlParams}`.replace(/\?$/, '');
window.history.replaceState({}, '', newUrl);
return course;
});
@@ -41,16 +45,20 @@ export default function Calendar(): JSX.Element {
async openCoursePopup({ data, sendResponse }) {
const course = activeSchedule.courses.find(course => course.uniqueId === data.uniqueId);
if (course === undefined) return;
setCourse(course);
setShowPopup(true);
sendResponse(await chrome.tabs.getCurrent());
const currentTab = await chrome.tabs.getCurrent();
if (currentTab === undefined) return;
sendResponse(currentTab);
},
});
listener.listen();
return () => listener.unlisten();
}, [activeSchedule.courses]);
}, [activeSchedule]);
useEffect(() => {
if (course) setShowPopup(true);
@@ -85,7 +93,7 @@ export default function Calendar(): JSX.Element {
</div>
<CourseCatalogInjectedPopup
course={course}
course={course!} // always defined when showPopup is true
onClose={() => setShowPopup(false)}
open={showPopup}
afterLeave={() => setCourse(null)}

View File

@@ -1,5 +1,4 @@
import type { Course } from '@shared/types/Course';
import type { CourseMeeting } from '@shared/types/CourseMeeting';
import React from 'react';
import styles from './CalendarCourseMeeting.module.scss';
@@ -12,28 +11,27 @@ export interface CalendarCourseMeetingProps {
course: Course;
/* index into course meeting array to display */
meetingIdx?: number;
/** The icon to display on the right side of the course. This is optional. */
rightIcon?: React.ReactNode;
}
/**
* `CalendarCourseMeeting` is a functional component that displays a course meeting.
*
* @example
* <CalendarCourseMeeting course={course} meeting={meeting} rightIcon={<Icon />} />
* <CalendarCourseMeeting course={course} meeting={meeting} />
*/
export default function CalendarCourseMeeting({
course,
meetingIdx,
rightIcon,
}: CalendarCourseMeetingProps): JSX.Element {
let meeting: CourseMeeting | null = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : null;
export default function CalendarCourseMeeting({ course, meetingIdx }: CalendarCourseMeetingProps): JSX.Element | null {
let meeting = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : undefined;
if (!meeting) {
return null;
}
return (
<div className={styles.component}>
<div className={styles.content}>
<div className={styles['course-detail']}>
<div className={styles.course}>
{course.department} {course.number} - {course.instructors[0].lastName}
{course.department} {course.number} - {course.instructors[0]?.lastName}
</div>
<div className={styles['time-and-location']}>
{`${meeting.getTimeString({ separator: '-', capitalize: true })}${

View File

@@ -1,3 +1,4 @@
import type { ThemeColor, TWIndex } from '@shared/types/ThemeColors';
import { getThemeColorHexByName } from '@shared/util/themeColors';
import Divider from '@views/components/common/Divider/Divider';
import React from 'react';
@@ -30,16 +31,19 @@ const baseColors = [
'fuchsia',
'pink',
'rose',
];
] as const;
const BaseColorNum = 500;
const StartingShadeIndex = 200;
const BaseColorNum: TWIndex = 500;
const StartingShadeIndex: TWIndex = 200;
const ShadeIncrement = 100;
const colorPatchColors = new Map<string, string[]>(
baseColors.map((baseColor: string) => [
baseColors.map(baseColor => [
theme.colors[baseColor][BaseColorNum],
Array.from({ length: 6 }, (_, index) => theme.colors[baseColor][StartingShadeIndex + ShadeIncrement * index]),
Array.from(
{ length: 6 },
(_, index) => theme.colors[baseColor][(StartingShadeIndex + ShadeIncrement * index) as TWIndex]
),
])
);
@@ -51,7 +55,7 @@ const hexCodeToBaseColor = new Map<string, string>(
* Props for the CourseCellColorPicker component.
*/
export interface CourseCellColorPickerProps {
setSelectedColor: React.Dispatch<React.SetStateAction<string | null>>;
setSelectedColor: React.Dispatch<React.SetStateAction<ThemeColor | null>>;
isInvertColorsToggled: boolean;
setIsInvertColorsToggled: React.Dispatch<React.SetStateAction<boolean>>;
}
@@ -89,7 +93,7 @@ export default function CourseCellColorPicker({
const [hexCode, setHexCode] = React.useState<string>(
getThemeColorHexByName('ut-gray').slice(1).toLocaleLowerCase()
);
const hexCodeWithHash = `#${hexCode}`;
const hexCodeWithHash = `#${hexCode}` as ThemeColor;
const selectedBaseColor = hexCodeToBaseColor.get(hexCodeWithHash);
const handleSelectColorPatch = (baseColor: string) => {
@@ -124,7 +128,7 @@ export default function CourseCellColorPicker({
)}
</button>
</div>
{hexCodeToBaseColor.has(hexCodeWithHash) && (
{selectedBaseColor && (
<>
<Divider orientation='horizontal' size='100%' className='my-1' />
<div className='grid grid-cols-6 gap-1'>

View File

@@ -26,7 +26,7 @@ function CalendarHour({ hour }: { hour: number }) {
}
function makeGridRow(row: number, cols: number): JSX.Element {
const hour = hoursOfDay[row];
const hour = hoursOfDay[row]!;
return (
<>
@@ -83,14 +83,17 @@ interface AccountForCourseConflictsProps {
// TODO: Deal with react strict mode (wacky movements)
function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseConflictsProps): JSX.Element[] {
// Groups by dayIndex to identify overlaps
const days = courseCells.reduce((acc, cell: CalendarGridCourse) => {
const { dayIndex } = cell.calendarGridPoint;
if (!acc[dayIndex]) {
acc[dayIndex] = [];
}
acc[dayIndex].push(cell);
return acc;
}, {});
const days = courseCells.reduce(
(acc, cell: CalendarGridCourse) => {
const { dayIndex } = cell.calendarGridPoint;
if (acc[dayIndex] === undefined) {
acc[dayIndex] = [];
}
acc[dayIndex]!.push(cell);
return acc;
},
{} as Record<number, CalendarGridCourse[]>
);
// Check for overlaps within each day and adjust gridColumnIndex and totalColumns
Object.values(days).forEach((dayCells: CalendarGridCourse[]) => {
@@ -121,7 +124,7 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC
});
return courseCells.map((block, i) => {
const { courseDeptAndInstr, timeAndLocation, status } = courseCells[i].componentProps;
const { courseDeptAndInstr, timeAndLocation, status } = courseCells[i]!.componentProps;
return (
<div
@@ -129,8 +132,8 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC
style={{
gridColumn: `${block.calendarGridPoint.dayIndex + 3}`,
gridRow: `${block.calendarGridPoint.startIndex} / ${block.calendarGridPoint.endIndex}`,
width: `calc(100% / ${block.totalColumns})`,
marginLeft: `calc(100% * ${(block.gridColumnStart - 1) / block.totalColumns})`,
width: `calc(100% / ${block.totalColumns ?? 1})`,
marginLeft: `calc(100% * ${((block.gridColumnStart ?? 0) - 1) / (block.totalColumns ?? 1)})`,
padding: '0px 10px 4px 0px',
}}
>

View File

@@ -1,4 +1,6 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import type { UserSchedule } from '@shared/types/UserSchedule';
import type { Serialized } from 'chrome-extension-toolkit';
import { toPng } from 'html-to-image';
export const CAL_MAP = {
@@ -13,9 +15,9 @@ export const CAL_MAP = {
/**
* Retrieves the schedule from the UserScheduleStore based on the active index.
* @returns {Promise<any>} A promise that resolves to the retrieved schedule.
* @returns A promise that resolves to the retrieved schedule.
*/
const getSchedule = async () => {
const getSchedule = async (): Promise<Serialized<UserSchedule> | undefined> => {
const schedules = await UserScheduleStore.get('schedules');
const activeIndex = await UserScheduleStore.get('activeIndex');
const schedule = schedules[activeIndex];
@@ -61,6 +63,10 @@ export const saveAsCal = async () => {
let icsString = 'BEGIN:VCALENDAR\nVERSION:2.0\nCALSCALE:GREGORIAN\nX-WR-CALNAME:My Schedule\n';
if (!schedule) {
throw new Error('No schedule found');
}
schedule.courses.forEach(course => {
course.schedule.meetings.forEach(meeting => {
const { startTime, endTime, days, location } = meeting;
@@ -85,7 +91,7 @@ export const saveAsCal = async () => {
icsString += `DTEND:${endDate}\n`;
icsString += `RRULE:FREQ=WEEKLY;BYDAY=${icsDays}\n`;
icsString += `SUMMARY:${course.fullName}\n`;
icsString += `LOCATION:${location.building} ${location.room}\n`;
icsString += `LOCATION:${location?.building ?? ''} ${location?.room ?? ''}\n`;
icsString += `END:VEVENT\n`;
});
});