Skip to content

Commit

Permalink
Merge branch 'master' into derek/popupInject
Browse files Browse the repository at this point in the history
  • Loading branch information
DereC4 committed Mar 15, 2024
2 parents 445b0de + 5714ed1 commit 5a85633
Show file tree
Hide file tree
Showing 63 changed files with 1,491 additions and 638 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_size = 4
indent_style = space
24 changes: 24 additions & 0 deletions .github/workflows/check-types.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Type Check

on: [push, pull_request]

jobs:
type-check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8

- name: Install dependencies
run: pnpm install

- name: Run tests
run: pnpm run check-types
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"prettier:fix": "prettier src --write",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives",
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --fix",
"check-types": "tsc --noEmit",
"test": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage",
Expand Down Expand Up @@ -116,4 +117,4 @@
"es-module-lexer": "^1.4.1"
}
}
}
}
18 changes: 18 additions & 0 deletions src/pages/background/background.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { BACKGROUND_MESSAGES } from '@shared/messages';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import updateBadgeText from '@shared/util/updateBadgeText';
import { MessageListener } from 'chrome-extension-toolkit';

import onInstall from './events/onInstall';
Expand Down Expand Up @@ -37,3 +39,19 @@ const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
});

messageListener.listen();

UserScheduleStore.listen('schedules', async schedules => {
const index = await UserScheduleStore.get('activeIndex');
const numCourses = schedules[index]?.courses?.length;
if (!numCourses) return;

updateBadgeText(numCourses);
});

UserScheduleStore.listen('activeIndex', async ({ newValue }) => {
const schedules = await UserScheduleStore.get('schedules');
const numCourses = schedules[newValue]?.courses?.length;
if (!numCourses) return;

updateBadgeText(numCourses);
});
1 change: 1 addition & 0 deletions src/pages/background/lib/addCourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default async function addCourse(scheduleName: string, course: Course): P
}

activeSchedule.courses.push(course);
activeSchedule.updatedAt = Date.now();

await UserScheduleStore.set('schedules', schedules);
}
2 changes: 2 additions & 0 deletions src/pages/background/lib/clearCourses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ export default async function clearCourses(scheduleName: string): Promise<void>
throw new Error(`Schedule ${scheduleName} does not exist`);
}
schedule.courses = [];
schedule.updatedAt = Date.now();

await UserScheduleStore.set('schedules', schedules);
}
1 change: 1 addition & 0 deletions src/pages/background/lib/createSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default async function createSchedule(scheduleName: string): Promise<stri
name: scheduleName,
courses: [],
hours: 0,
updatedAt: Date.now(),
});

await UserScheduleStore.set('schedules', schedules);
Expand Down
1 change: 1 addition & 0 deletions src/pages/background/lib/removeCourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default async function removeCourse(scheduleName: string, course: Course)
}

activeSchedule.courses = activeSchedule.courses.filter(c => c.uniqueId !== course.uniqueId);
activeSchedule.updatedAt = Date.now();

await UserScheduleStore.set('schedules', schedules);
}
1 change: 1 addition & 0 deletions src/pages/background/lib/renameSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default async function renameSchedule(scheduleName: string, newName: stri
}

schedules[scheduleIndex].name = newName;
schedules[scheduleIndex].updatedAt = Date.now();

await UserScheduleStore.set('schedules', schedules);
return undefined;
Expand Down
1 change: 1 addition & 0 deletions src/pages/background/lib/switchSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default async function switchSchedule(scheduleName: string): Promise<void
if (scheduleIndex === -1) {
throw new Error(`Schedule ${scheduleName} does not exist`);
}
schedules[scheduleIndex].updatedAt = Date.now();

await UserScheduleStore.set('activeIndex', scheduleIndex);
}
8 changes: 8 additions & 0 deletions src/pages/popup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Popup</title>
<style>
body {
margin: 0;
padding: 0;
width: 360px;
height: 540px;
}
</style>
</head>

<body>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/popup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'uno.css';

import PopupMain from '@views/components/PopupMain';
import React from 'react';
import { createRoot } from 'react-dom/client';
Expand Down
1 change: 1 addition & 0 deletions src/shared/storage/UserScheduleStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const UserScheduleStore = createLocalStore<IUserScheduleStore>({
courses: [],
name: 'Schedule 1',
hours: 0,
updatedAt: Date.now(),
}),
],
activeIndex: 0,
Expand Down
5 changes: 5 additions & 0 deletions src/shared/types/Course.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,16 @@ export class Course {
instructionMode: InstructionMode;
/** Which semester is the course from */
semester: Semester;
/** Unix timestamp of when the course was last scraped */
scrapedAt: number;

constructor(course: Serialized<Course>) {
Object.assign(this, course);
this.schedule = new CourseSchedule(course.schedule);
this.instructors = course.instructors.map(i => new Instructor(i));
if (!course.scrapedAt) {
this.scrapedAt = Date.now();
}
}

/**
Expand Down
23 changes: 10 additions & 13 deletions src/shared/types/CourseSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ import { CourseMeeting, DAY_MAP } from './CourseMeeting';
* This represents the schedule for a course, which includes all the meeting times for the course, as well as helper functions for parsing, serializing, and deserializing the schedule
*/
export class CourseSchedule {
meetings: CourseMeeting[];
meetings: CourseMeeting[] = [];

constructor(courseSchedule?: Serialized<CourseSchedule>) {
if (!courseSchedule) {
if (!courseSchedule || courseSchedule.meetings === undefined) {
this.meetings = [];
return;
}
Object.assign(this, courseSchedule);
this.meetings = [];
for (let meeting of courseSchedule.meetings) {
this.meetings.push(new CourseMeeting(meeting));
}

this.meetings = courseSchedule.meetings.map(meeting => new CourseMeeting(meeting));
}

/**
Expand Down Expand Up @@ -48,13 +46,12 @@ export class CourseSchedule {
.replaceAll('.', '')
.split('-')
.map(time => {
const [hour, rest] = time.split(':');
const [minute, ampm] = rest.split(' ');
const [rawHour, rest] = time.split(':');
const [rawMinute, ampm] = rest.split(' ');
const hour = (rawHour === '12' ? 0 : Number(rawHour)) + (ampm === 'pm' ? 12 : 0);
const minute = Number(rawMinute);

if (ampm === 'pm') {
return Number(hour) * 60 + Number(minute) + 12 * 60;
}
return Number(hour) * 60 + Number(minute);
return hour * 60 + minute;
});

const location = locLine.split(' ').filter(Boolean);
Expand Down
3 changes: 3 additions & 0 deletions src/shared/types/UserSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export class UserSchedule {
courses: Course[];
name: string;
hours: number;
/** Unix timestamp of when the schedule was last updated */
updatedAt: number;

constructor(schedule: Serialized<UserSchedule>) {
this.courses = schedule.courses.map(c => new Course(c));
Expand All @@ -17,6 +19,7 @@ export class UserSchedule {
for (const course of this.courses) {
this.hours += course.creditHours;
}
this.updatedAt = schedule.updatedAt;
}

containsCourse(course: Course): boolean {
Expand Down
34 changes: 34 additions & 0 deletions src/shared/util/updateBadgeText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { colors } from './themeColors';
import { MILLISECOND } from './time';

/** How long should we flash the badge when it changes value */
export const POPUP_FLASH_TIME = 200 * MILLISECOND;

/** The maximum number to show in the badge, after which we show a '+' */
export const BADGE_LIMIT = 10;

/**
* Updates the badge text based on the given value.
* If the value is greater than the badge limit, it sets the badge text to the badge limit followed by a '+'.
* @param value - The value to be displayed in the badge.
*/
export default function updateBadgeText(value: number): void {
let badgeText = '';
if (value > 0) {
if (value > BADGE_LIMIT) {
badgeText = `${BADGE_LIMIT}+`;
} else {
badgeText = `${value}`;
}
}
chrome.action.setBadgeText({ text: badgeText });
flashBadgeColor();
}

/**
* Flashes the badge color by setting the badge background color to a color and then resetting it after a short delay.
*/
function flashBadgeColor() {
chrome.action.setBadgeBackgroundColor({ color: colors.ut.burntorange });
setTimeout(() => chrome.action.setBadgeBackgroundColor({ color: colors.ut.orange }), POPUP_FLASH_TIME);
}
2 changes: 2 additions & 0 deletions src/stories/components/ConflictsWithWarning.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const ExampleCourse: Course = new Course({
status: Status.WAITLISTED,
uniqueId: 12345,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
scrapedAt: Date.now(),
});
export const ExampleCourse2: Course = new Course({
courseName: 'PRINCIPLES OF COMPUTER SYSTEMS',
Expand Down Expand Up @@ -90,6 +91,7 @@ export const ExampleCourse2: Course = new Course({
status: Status.WAITLISTED,
uniqueId: 67890,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
scrapedAt: Date.now(),
});

const meta = {
Expand Down
52 changes: 35 additions & 17 deletions src/stories/components/Dropdown.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type { Meta, StoryObj } from '@storybook/react';
import List from '@views/components/common/List/List';
import ScheduleDropdown from '@views/components/common/ScheduleDropdown/ScheduleDropdown';
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
import useSchedules, { switchSchedule } from '@views/hooks/useSchedules';
import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules';
import type { Serialized } from 'chrome-extension-toolkit';
import React from 'react';
import React, { useEffect } from 'react';

import { exampleSchedule } from '../injected/mocked';

Expand Down Expand Up @@ -47,27 +47,45 @@ const meta: Meta<typeof ScheduleDropdown> = {
},
},
},
render: (args: any) => (
<div className='w-80'>
<ScheduleDropdown {...args}>
<List
draggableElements={schedules.map((schedule, index) => {
const [activeSchedule] = useSchedules();
return (
render: (args: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [activeSchedule, schedules] = useSchedules();

// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
console.log(activeSchedule);
}, [activeSchedule]);

return (
<div className='w-80'>
<ScheduleDropdown {...args}>
<List
draggables={schedules}
equalityCheck={(a, b) => a.name === b.name}
onReordered={reordered => {
const activeSchedule = getActiveSchedule();
const activeIndex = reordered.findIndex(s => s.name === activeSchedule.name);

// don't care about the promise
UserScheduleStore.set('schedules', reordered);
UserScheduleStore.set('activeIndex', activeIndex);
}}
gap={10}
>
{(schedule, handleProps) => (
<ScheduleListItem
active={activeSchedule?.name === schedule.name}
name={schedule.name}
onClick={() => {
switchSchedule(schedule.name);
}}
dragHandleProps={handleProps}
/>
);
})}
gap={10}
/>
</ScheduleDropdown>
</div>
),
)}
</List>
</ScheduleDropdown>
</div>
);
},
} satisfies Meta<typeof ScheduleDropdown>;
export default meta;

Expand Down
Loading

0 comments on commit 5a85633

Please sign in to comment.