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:
@@ -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)}
|
||||
|
||||
@@ -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 })}${
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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`;
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user