Skip to content

Commit

Permalink
feat: optimize calendar (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinWu098 committed Dec 8, 2024
1 parent b8dd5f6 commit 1c690f3
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 68 deletions.
91 changes: 40 additions & 51 deletions apps/antalmanac/src/components/Calendar/CalendarRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'react-big-calendar/lib/css/react-big-calendar.css';
import './calendar.css';

import { Box, ClickAwayListener, Popper } from '@material-ui/core';
import { Box, Popover } from '@material-ui/core';
import moment from 'moment';
import { memo, SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { Calendar, DateLocalizer, momentLocalizer, Views } from 'react-big-calendar';
import { shallow } from 'zustand/shallow';

Expand All @@ -14,17 +14,16 @@ import { CalendarCourseEvent } from '$components/Calendar/calendar-course-event'
import { getDefaultFinalsStartDate, getFinalsStartDateForTerm } from '$lib/termData';
import AppStore from '$stores/AppStore';
import { useHoveredStore } from '$stores/HoveredStore';
import { useSelectedEventStore } from '$stores/SelectedEventStore';
import { useTimeFormatStore } from '$stores/SettingsStore';

const localizer = momentLocalizer(moment);
const views = [Views.WEEK, Views.WORK_WEEK];
const components = { event: CalendarCourseEvent };
const max = new Date(2018, 0, 1, 23);

export const ScheduleCalendar = memo(() => {
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const [showFinalsSchedule, setShowFinalsSchedule] = useState(false);
const [courseInMoreInfo, setCourseInMoreInfo] = useState<CalendarEvent | null>(null);
const [calendarEventKey, setCalendarEventKey] = useState<number | null>(null);
const [eventsInCalendar, setEventsInCalendar] = useState(() => AppStore.getEventsInCalendar());
const [finalsEventsInCalendar, setFinalEventsInCalendar] = useState(() => AppStore.getFinalEventsInCalendar());
const [currentScheduleIndex, setCurrentScheduleIndex] = useState(() => AppStore.getCurrentScheduleIndex());
Expand All @@ -35,6 +34,10 @@ export const ScheduleCalendar = memo(() => {
(state) => [state.hoveredCalendarizedCourses, state.hoveredCalendarizedFinal],
shallow
);
const [anchorEl, selectedEvent, setSelectedEvent] = useSelectedEventStore(
(state) => [state.selectedEventAnchorEl, state.selectedEvent, state.setSelectedEvent],
shallow
);

const getEventsForCalendar = useCallback((): CalendarEvent[] => {
if (showFinalsSchedule)
Expand All @@ -54,25 +57,14 @@ export const ScheduleCalendar = memo(() => {
const events = useMemo(() => getEventsForCalendar(), [getEventsForCalendar]);

const handleClosePopover = useCallback(() => {
setAnchorEl(null);
}, []);
setSelectedEvent(null, null);
}, [setSelectedEvent]);

const toggleDisplayFinalsSchedule = useCallback(() => {
handleClosePopover();
setShowFinalsSchedule((prevState) => !prevState);
}, [handleClosePopover]);

const handleEventClick = useCallback((event: CalendarEvent, e: SyntheticEvent<HTMLElement, Event>) => {
const { currentTarget } = e;
e.stopPropagation();

if (event.isCustomEvent || event.sectionType !== 'Fin') {
setAnchorEl((prevAnchorEl) => (prevAnchorEl === currentTarget ? null : currentTarget));
setCourseInMoreInfo(event);
setCalendarEventKey(Math.random());
}
}, []);

/**
* Finds the earliest start time and returns that or 7AM, whichever is earlier
* @returns A date with the earliest time or 7AM
Expand Down Expand Up @@ -198,35 +190,27 @@ export const ScheduleCalendar = memo(() => {
/>

<Box id="screenshot" height="0" flexGrow={1}>
<Popper
anchorEl={anchorEl}
placement="right"
modifiers={{
offset: {
enabled: true,
offset: '0, 10',
},
flip: {
enabled: true,
},
preventOverflow: {
enabled: true,
boundariesElement: 'scrollParent',
},
}}
open={Boolean(anchorEl)}
>
<ClickAwayListener onClickAway={handleClosePopover}>
<Box>
<CourseCalendarEvent
key={calendarEventKey}
closePopover={handleClosePopover}
courseInMoreInfo={courseInMoreInfo as CalendarEvent}
scheduleNames={scheduleNames}
/>
</Box>
</ClickAwayListener>
</Popper>
{selectedEvent ? (
<Popover
anchorEl={anchorEl}
open={Boolean(anchorEl) && !!selectedEvent}
onClose={handleClosePopover}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<CourseCalendarEvent
closePopover={handleClosePopover}
selectedEvent={selectedEvent}
scheduleNames={scheduleNames}
/>
</Popover>
) : null}

<Calendar<CalendarEvent, object>
localizer={localizer}
Expand All @@ -235,18 +219,23 @@ export const ScheduleCalendar = memo(() => {
views={views}
defaultView={Views.WORK_WEEK}
view={hasWeekendCourse ? Views.WEEK : Views.WORK_WEEK}
onView={undefined}
onView={() => {
return;
}}
step={15}
timeslots={2}
date={date}
onNavigate={undefined}
onNavigate={() => {
return;
}}
min={getStartTime()}
max={new Date(2018, 0, 1, 23)}
max={max}
events={events}
eventPropGetter={eventStyleGetter}
showMultiDayTimes={false}
components={components}
onSelectEvent={handleEventClick}
onSelectEvent={undefined}
onSelectSlot={undefined}
/>
</Box>
</Box>
Expand Down
26 changes: 13 additions & 13 deletions apps/antalmanac/src/components/Calendar/CourseCalendarEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { formatTimes } from '$stores/calendarizeHelpers';
const styles: Styles<Theme, object> = {
courseContainer: {
padding: '0.5rem',
margin: '0 1rem',
margin: '0 0.5rem',
minWidth: '15rem',
},
customEventContainer: {
Expand Down Expand Up @@ -125,7 +125,7 @@ export interface CourseEvent extends CommonCalendarEvent {
}

/**
* There is another CustomEvent interface in CourseCalendarEvent and they are slightly different. The this one represents only one day, like the event on Monday, and needs to be duplicated to be repeated across multiple days. The other one, `CustomEventDialog`'s `RepeatingCustomEvent`, encapsulates the occurences of an event on multiple days, like Monday Tuesday Wednesday all in the same object as specified by the `days` array.
* There is another CustomEvent interface in CourseCalendarEvent and they are slightly different. The this one represents only one day, like the event on Monday, and needs to be duplicated to be repeated across multiple days. The other one, `CustomEventDialog`'s `RepeatingCustomEvent`, encapsulates the occurrences of an event on multiple days, like Monday Tuesday Wednesday all in the same object as specified by the `days` array.
* https://github.com/icssc/AntAlmanac/wiki/The-Great-AntAlmanac-TypeScript-Rewritening%E2%84%A2#duplicate-interface-names-%EF%B8%8F
*/
export interface CustomEvent extends CommonCalendarEvent {
Expand All @@ -139,7 +139,7 @@ export type CalendarEvent = CourseEvent | CustomEvent;

interface CourseCalendarEventProps {
classes: ClassNameMap;
courseInMoreInfo: CalendarEvent;
selectedEvent: CalendarEvent;
scheduleNames: string[];
closePopover: () => void;
}
Expand Down Expand Up @@ -172,10 +172,10 @@ const CourseCalendarEvent = (props: CourseCalendarEventProps) => {
setActiveTab(2);
}, [setActiveTab]);

const { classes, courseInMoreInfo } = props;
const { classes, selectedEvent } = props;

if (!courseInMoreInfo.isCustomEvent) {
const { term, instructors, sectionCode, title, finalExam, locations, sectionType } = courseInMoreInfo;
if (!selectedEvent.isCustomEvent) {
const { term, instructors, sectionCode, title, finalExam, locations, sectionType } = selectedEvent;

let finalExamString = '';

Expand Down Expand Up @@ -269,10 +269,10 @@ const CourseCalendarEvent = (props: CourseCalendarEventProps) => {
<td>Color</td>
<td className={`${classes.colorPicker} ${classes.stickToRight}`}>
<ColorPicker
color={courseInMoreInfo.color}
isCustomEvent={courseInMoreInfo.isCustomEvent}
sectionCode={courseInMoreInfo.sectionCode}
term={courseInMoreInfo.term}
color={selectedEvent.color}
isCustomEvent={selectedEvent.isCustomEvent}
sectionCode={selectedEvent.sectionCode}
term={selectedEvent.term}
analyticsCategory={analyticsEnum.calendar.title}
/>
</td>
Expand All @@ -282,7 +282,7 @@ const CourseCalendarEvent = (props: CourseCalendarEventProps) => {
</Paper>
);
} else {
const { title, customEventID, building } = courseInMoreInfo;
const { title, customEventID, building } = selectedEvent;
return (
<Paper className={classes.customEventContainer} ref={paperRef}>
<div className={classes.title}>{title}</div>
Expand All @@ -301,9 +301,9 @@ const CourseCalendarEvent = (props: CourseCalendarEventProps) => {
<div className={classes.buttonBar}>
<div className={`${classes.colorPicker}`}>
<ColorPicker
color={courseInMoreInfo.color}
color={selectedEvent.color}
isCustomEvent={true}
customEventID={courseInMoreInfo.customEventID}
customEventID={selectedEvent.customEventID}
analyticsCategory={analyticsEnum.calendar.title}
/>
</div>
Expand Down
20 changes: 16 additions & 4 deletions apps/antalmanac/src/components/Calendar/calendar-course-event.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { Box } from '@material-ui/core';
import { memo } from 'react';
import { shallow } from 'zustand/shallow';

import { CalendarEvent } from '$components/Calendar/CourseCalendarEvent';
import locationIds from '$lib/location_ids';
import { useSelectedEventStore } from '$stores/SelectedEventStore';

export const CalendarCourseEvent = memo(({ event }: { event: CalendarEvent }) => {
const setSelectedEvent = useSelectedEventStore((state) => state.setSelectedEvent, shallow);

const handleClick = (e: React.MouseEvent) => {
console.log('what');
e.preventDefault();
e.stopPropagation();

setSelectedEvent(e, event);
};

if (event.isCustomEvent) {
return (
<Box>
<Box onClick={handleClick}>
<Box
style={{
display: 'flex',
Expand All @@ -28,7 +40,7 @@ export const CalendarCourseEvent = memo(({ event }: { event: CalendarEvent }) =>
}

return (
<Box>
<Box onClick={handleClick}>
<Box
style={{
display: 'flex',
Expand All @@ -46,8 +58,8 @@ export const CalendarCourseEvent = memo(({ event }: { event: CalendarEvent }) =>
{event.showLocationInfo
? event.locations.map((location) => `${location.building} ${location.room}`).join(', ')
: event.locations.length > 1
? `${event.locations.length} Locations`
: `${event.locations[0].building} ${event.locations[0].room}`}
? `${event.locations.length} Locations`
: `${event.locations[0].building} ${event.locations[0].room}`}
</Box>
<Box>{event.sectionCode}</Box>
</Box>
Expand Down
23 changes: 23 additions & 0 deletions apps/antalmanac/src/stores/SelectedEventStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SyntheticEvent } from 'react';
import { create } from 'zustand';

import { CalendarEvent } from '$components/Calendar/CourseCalendarEvent';

export interface SelectedEventStore {
selectedEvent: CalendarEvent | null;
selectedEventAnchorEl: Element | null;
setSelectedEvent: (anchorEl: SyntheticEvent | null, selectedEvent: CalendarEvent | null) => void;
}

export const useSelectedEventStore = create<SelectedEventStore>((set) => {
return {
selectedEvent: null,
selectedEventAnchorEl: null,
setSelectedEvent: (anchorEl: SyntheticEvent | null, selectedEvent: CalendarEvent | null) => {
set({
selectedEvent: selectedEvent,
selectedEventAnchorEl: anchorEl?.currentTarget,
});
},
};
});

0 comments on commit 1c690f3

Please sign in to comment.