Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple schedule selection #320

Merged
merged 13 commits into from
Mar 31, 2024
2 changes: 2 additions & 0 deletions src/components/AppDataLoader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ function ContextProvider({
allFriends,
currentFriends: scheduleVersion.friends ?? {},
...castDraft(scheduleVersion.schedule),
versions: termScheduleData.versions,
},
{
setTerm,
Expand Down Expand Up @@ -511,6 +512,7 @@ function ContextProvider({
deleteVersion,
renameVersion,
cloneVersion,
termScheduleData.versions,
]
);

Expand Down
38 changes: 27 additions & 11 deletions src/components/Calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useContext } from 'react';
import { Immutable } from 'immer';

import { FriendScheduleData } from '../../data/types';
import { Section } from '../../data/beans';
import { CLOSE, DAYS, OPEN } from '../../constants';
import { classes, timeToShortString } from '../../utils/misc';
Expand Down Expand Up @@ -59,7 +61,7 @@ export default function Calendar({
overlayFriendSchedules = [],
isAutosized = false,
}: CalendarProps): React.ReactElement {
const [{ pinnedCrns, oscar, events, currentVersion }] =
const [{ pinnedCrns, oscar, events, currentVersion, versions }] =
useContext(ScheduleContext);

const [{ friends }] = useContext(FriendContext);
Expand Down Expand Up @@ -107,9 +109,10 @@ export default function Calendar({
});
};

const crns = pinSelf
? Array.from(new Set([...pinnedCrns, ...(overlayCrns || [])]))
: [];
const crns =
pinSelf && !compare
? Array.from(new Set([...pinnedCrns, ...(overlayCrns || [])]))
: [];

// Find section using crn and convert the meetings into
// an array of CommonMeetingObject
Expand Down Expand Up @@ -154,10 +157,23 @@ export default function Calendar({
a.period.end - a.period.start - (b.period.end - b.period.start) ?? 0
);

const friendSchedules: { data: FriendCrnData; overlay: boolean }[] = [];
const friendEvents: { data: FriendEventData; overlay: boolean }[] = [];
const userSchedules: { data: FriendCrnData; overlay: boolean }[] = [];
const userEvents: { data: FriendEventData; overlay: boolean }[] = [];
if (compare) {
Object.values(friends).forEach((friend) =>
/*
Create a dummy friend schedule data object for self schedules for
conforming types to iterate over all schedules in one go
*/
const selfFriend: Immutable<FriendScheduleData> = {
self: {
name: 'Me',
email: '',
versions,
},
};
const allUsers = { ...friends, ...selfFriend };

Object.values(allUsers).forEach((friend) =>
Object.entries(friend.versions)
.filter(
(schedule) =>
Expand All @@ -167,7 +183,7 @@ export default function Calendar({
.forEach((schedule) => {
const friendMeetings: CommonMeetingObject[] = [];
schedule[1].schedule.pinnedCrns.forEach((crn) => {
friendSchedules.push({
userSchedules.push({
data: {
friend: friend.name,
scheduleName: schedule[1].name,
Expand All @@ -191,7 +207,7 @@ export default function Calendar({
});
});
schedule[1].schedule.events.forEach((event) => {
friendEvents.push({
userEvents.push({
data: {
friend: friend.name,
scheduleName: schedule[1].name,
Expand Down Expand Up @@ -423,7 +439,7 @@ export default function Calendar({
/>
))}
{compare &&
friendSchedules.map(({ data, overlay }) => (
userSchedules.map(({ data, overlay }) => (
<CompareBlocks
key={`${data.scheduleId}-${data.crn}`}
crn={data.crn}
Expand Down Expand Up @@ -457,7 +473,7 @@ export default function Calendar({
/>
))}
{compare &&
friendEvents.map(({ data, overlay }) => (
userEvents.map(({ data, overlay }) => (
<EventBlocks
key={`${data.scheduleId}-${data.id}`}
event={data.event}
Expand Down
46 changes: 32 additions & 14 deletions src/components/ComparisonContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,19 @@ export type ComparisonContainerProps = {
overlaySchedules?: string[]
) => void;
pinnedSchedules: string[];
pinSelf: boolean;
shareBackRemount: number;
};

export default function ComparisonContainer({
handleCompareSchedules,
pinnedSchedules,
pinSelf,
shareBackRemount,
}: ComparisonContainerProps): React.ReactElement {
const [selected, setSelected] = useState<string[]>(pinnedSchedules);
const [deleteConfirm, setDeleteConfirm] = useState<DeleteInfo>(null);
const [editInfo, setEditInfo] = useState<EditInfo>(null);
const [editValue, setEditValue] = useState('');
const [paletteInfo, setPaletteInfo] = useState<string>();
const [scheduleSelected, setScheduleSelected] = useState(pinSelf);
const [invitationModalOpen, setInvitationModalOpen] = useState(false);
const [invitationModalEmail, setInvitationModalEmail] = useState('');

Expand All @@ -98,6 +95,12 @@ export default function ComparisonContainer({

useEffect(() => {
const newColorMap = { ...colorMap };
allVersionNames.forEach((versionName) => {
const version = versionName.id;
if (!(version in newColorMap)) {
newColorMap[version] = getRandomColor();
}
});
if (!(currentVersion in newColorMap)) {
newColorMap[currentVersion] = getRandomColor();
}
Expand All @@ -114,7 +117,7 @@ export default function ComparisonContainer({
if (Object.keys(newColorMap).length !== Object.keys(colorMap).length) {
patchSchedule({ colorMap: newColorMap });
}
}, [friends, currentVersion, colorMap, patchSchedule]);
}, [friends, currentVersion, colorMap, patchSchedule, allVersionNames]);

const handleEdit = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -286,22 +289,19 @@ export default function ComparisonContainer({
<div className="my-schedule">
<p className="content-title">My Schedule</p>
{allVersionNames
.filter((version) => version.id === currentVersion)
// .filter((version) => version.id === currentVersion)
.map((version) => {
return (
<ScheduleRow
key={version.id}
id={version.id}
type="Version"
onClick={(): void => {
setScheduleSelected(!scheduleSelected);
handleCompareSchedules(
undefined,
undefined,
!scheduleSelected
);
handleToggleSchedule(version.id);
}}
checkboxColor={scheduleSelected ? colorMap[version.id] : ''}
checkboxColor={
selected.includes(version.id) ? colorMap[version.id] : ''
}
name={version.name}
// placeholder functions
handleEditSchedule={(): void => {
Expand Down Expand Up @@ -334,6 +334,24 @@ export default function ComparisonContainer({
paletteInfo={paletteInfo}
setPaletteInfo={setPaletteInfo}
handleNameEditOnBlur={handleNameEditOnBlur}
hoverFriendSchedule={(): void => {
handleCompareSchedules(
undefined,
undefined,
undefined,
undefined,
[version.id]
);
}}
unhoverFriendSchedule={(): void => {
handleCompareSchedules(
undefined,
undefined,
undefined,
undefined,
[]
);
}}
/>
);
})}
Expand Down Expand Up @@ -559,12 +577,12 @@ function ScheduleRow({
<div
className="schedule-row"
onMouseEnter={(): void => {
if (type === 'Schedule') {
if (type === 'Schedule' || type === 'Version') {
hoverFriendSchedule?.();
}
}}
onMouseLeave={(): void => {
if (type === 'Schedule') {
if (type === 'Schedule' || type === 'Version') {
unhoverFriendSchedule?.();
}
}}
Expand Down
3 changes: 0 additions & 3 deletions src/components/ComparisonPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ export type ComparisonPanelProps = {
overlaySchedules?: string[]
) => void;
pinnedSchedules: string[];
pinSelf: boolean;
compare: boolean;
expanded: boolean;
};

export default function ComparisonPanel({
handleCompareSchedules,
pinnedSchedules,
pinSelf,
compare,
expanded,
}: ComparisonPanelProps): React.ReactElement {
Expand Down Expand Up @@ -138,7 +136,6 @@ export default function ComparisonPanel({
<ComparisonContainer
handleCompareSchedules={handleCompareSchedules}
pinnedSchedules={pinnedSchedules}
pinSelf={pinSelf}
shareBackRemount={shareBackRemount}
/>
</div>
Expand Down
13 changes: 9 additions & 4 deletions src/components/Scheduler/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useContext, useMemo, useState } from 'react';

import { classes } from '../../utils/misc';
import {
Expand All @@ -8,7 +8,11 @@ import {
ComparisonPanel,
CourseContainer,
} from '..';
import { OverlayCrnsContext, OverlayCrnsContextValue } from '../../contexts';
import {
OverlayCrnsContext,
OverlayCrnsContextValue,
ScheduleContext,
} from '../../contexts';
import { DESKTOP_BREAKPOINT } from '../../constants';
import useCompareStateFromStorage from '../../data/hooks/useCompareStateFromStorage';
import useScreenWidth from '../../hooks/useScreenWidth';
Expand All @@ -31,8 +35,10 @@ export default function Scheduler(): React.ReactElement {
[overlayCrns, setOverlayCrns]
);

const [{ currentVersion }] = useContext(ScheduleContext);

const { compare, pinned, pinSelf, expanded, setCompareState } =
useCompareStateFromStorage();
useCompareStateFromStorage({ pinDefault: [currentVersion] });
const [overlaySchedules, setOverlaySchedules] = useState<string[]>([]);

const handleCompareSchedules = useCallback(
Expand Down Expand Up @@ -86,7 +92,6 @@ export default function Scheduler(): React.ReactElement {
<ComparisonPanel
handleCompareSchedules={handleCompareSchedules}
pinnedSchedules={pinned}
pinSelf={pinSelf}
compare={compare}
expanded={expanded}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ const CAMPUSES: Record<string, string> = {

const BACKEND_BASE_URL = 'https://gt-scheduler.azurewebsites.net';
const FIREBASE_PROJECT_ID = firebaseConfig.projectId || `gt-scheduler-web-dev`;
const CLOUD_FUNCTION_BASE_URL = `https://us-central1-${FIREBASE_PROJECT_ID}.cloudfunctions.net`;
// const CLOUD_FUNCTION_BASE_URL = `http://127.0.0.1:5001/${FIREBASE_PROJECT_ID}/us-central1`;
// const CLOUD_FUNCTION_BASE_URL = `https://us-central1-${FIREBASE_PROJECT_ID}.cloudfunctions.net`;
const CLOUD_FUNCTION_BASE_URL = `http://127.0.0.1:5001/${FIREBASE_PROJECT_ID}/us-central1`;

const LARGE_DESKTOP_BREAKPOINT = 1200;
const DESKTOP_BREAKPOINT = 1024;
Expand Down
12 changes: 10 additions & 2 deletions src/contexts/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { Draft, Immutable } from 'immer';

import { Oscar } from '../data/beans';
import { EMPTY_OSCAR } from '../data/beans/Oscar';
import { defaultSchedule, FriendShareData, Schedule } from '../data/types';
import {
defaultSchedule,
FriendShareData,
Schedule,
TermScheduleData,
} from '../data/types';
import { ErrorWithFields } from '../log';

type ExtraData = {
Expand All @@ -17,7 +22,9 @@ type ExtraData = {

export type ScheduleContextData = Immutable<Schedule> &
// `Oscar` can't go into `Immutable`, so we place it separately
Immutable<ExtraData> & { readonly oscar: Oscar };
Immutable<ExtraData> & {
readonly oscar: Oscar;
} & Immutable<TermScheduleData>;

export type ScheduleContextSetters = {
setTerm: (next: string) => void;
Expand Down Expand Up @@ -45,6 +52,7 @@ export const ScheduleContext = React.createContext<ScheduleContextValue>([
allFriends: {},
oscar: EMPTY_OSCAR,
...defaultSchedule,
versions: {},
},
{
setTerm: (next: string): void => {
Expand Down
22 changes: 17 additions & 5 deletions src/data/hooks/useCompareStateFromStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ type HookResult = {
) => void;
};

type Props = {
compareDefault?: boolean;
pinDefault?: string[];
pinSelfDefault?: boolean;
expandedDefault?: boolean;
};

/**
* Gets the current UI state from local storage.
* Do not call this function in a non-root component;
Expand All @@ -24,32 +31,37 @@ type HookResult = {
* with different schedules if desired,
* but still have the app resume to the last viewed schedule when opened again.
*/
export default function useCompareStateFromStorage(): HookResult {
export default function useCompareStateFromStorage({
compareDefault,
pinDefault,
pinSelfDefault,
expandedDefault,
}: Props): HookResult {
const [compare, setCompare] = useLocalStorageState<boolean>(
'compare-panel-state-compareValue',
{
defaultValue: false,
defaultValue: compareDefault ?? false,
storageSync: false,
}
);
const [pinned, setPinned] = useLocalStorageState<string[]>(
'compare-panel-state-pinnedSchedules',
{
defaultValue: [],
defaultValue: pinDefault ?? [],
storageSync: false,
}
);
const [pinSelf, setPinSelf] = useLocalStorageState<boolean>(
'compare-panel-state-pinSelfValue',
{
defaultValue: true,
defaultValue: pinSelfDefault ?? true,
storageSync: false,
}
);
const [expanded, setExpanded] = useLocalStorageState<boolean>(
'compare-panel-state-expandedValue',
{
defaultValue: true,
defaultValue: expandedDefault ?? true,
storageSync: false,
}
);
Expand Down
Loading