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

feat: settings page #260

Merged
merged 24 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f0138e9
feat: setup settings page boilerplate
doprz Oct 3, 2024
bbabe45
feat: split view into halves
doprz Oct 3, 2024
f424f62
feat: add preview for Customization Options section
doprz Oct 3, 2024
2e3332d
feat: add OptionStore logic and LD icon
doprz Oct 4, 2024
4e632f3
feat: add courseStatusChips functionality
doprz Oct 4, 2024
c9086eb
feat: migrate experimental settings to proper settings
doprz Oct 4, 2024
d123977
feat: center Preview children and add className override
doprz Oct 4, 2024
713d2bb
feat: add GitHub stats
doprz Oct 5, 2024
0c4be2e
feat: open GitHub user profile onclick
doprz Oct 5, 2024
f0b5519
feat: get user GitHub stats
doprz Oct 5, 2024
5a323a8
feat: refactor into useGitHubStats hook
doprz Oct 5, 2024
65b6c52
feat: toggle GitHub stats when the user presses the 'S' key
doprz Oct 5, 2024
ca42cb5
chore: update title
doprz Oct 5, 2024
642083d
fix: remove extra file
doprz Oct 5, 2024
f5b3145
feat: refactor and add DialogProvider
doprz Oct 5, 2024
0a5cd29
fix: import
doprz Oct 5, 2024
698990d
test: this commit has issues
doprz Oct 5, 2024
505243e
fix: no schedule bug
doprz Oct 5, 2024
39c59ff
fix: longhorn developers icon not rendering in prod builds
doprz Oct 5, 2024
a05ab17
feat(pr-review): fix UI and comment out experimental code
doprz Oct 7, 2024
53ed2f0
chore: fix merge conflict
doprz Oct 7, 2024
dae6964
chore: run lint and prettier
doprz Oct 7, 2024
efd5199
feat: add responsive design
doprz Oct 9, 2024
c7702f0
feat: use @octokit/rest and fix GitHub stats
doprz Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"highcharts-react-official": "^3.2.1",
"html-to-image": "^1.11.11",
"husky": "^9.1.6",
"kc-dabr-wasm": "^0.1.2",
"nanoid": "^5.0.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand All @@ -49,6 +50,7 @@
"@commitlint/types": "^19.5.0",
"@crxjs/vite-plugin": "2.0.0-beta.21",
"@iconify-json/bi": "^1.2.0",
"@iconify-json/iconoir": "^1.2.1",
"@iconify-json/material-symbols": "^1.2.2",
"@iconify-json/ri": "^1.2.0",
"@storybook/addon-designs": "^8.0.3",
Expand Down
1,264 changes: 643 additions & 621 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

Binary file added src/assets/LD-icon.png
doprz marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/debug/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DevStore } from '@shared/storage/DevStore';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React, { useEffect } from 'react';
import { createRoot } from 'react-dom/client';

Expand Down Expand Up @@ -77,6 +78,7 @@ function DevDashboard() {
const [localStorage, setLocalStorage] = React.useState<Record<string, unknown>>({});
const [syncStorage, setSyncStorage] = React.useState<Record<string, unknown>>({});
const [sessionStorage, setSessionStorage] = React.useState<Record<string, unknown>>({});
useKC_DABR_WASM();

useEffect(() => {
const onVisibilityChange = () => {
Expand Down
13 changes: 13 additions & 0 deletions src/pages/background/lib/deleteSchedule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';

import createSchedule from './createSchedule';

/**
* Deletes a schedule with the specified name.
*
Expand Down Expand Up @@ -32,3 +34,14 @@ export default async function deleteSchedule(scheduleId: string): Promise<string
}
return undefined;
}

/**
* Deletes all schedules.
*
* @returns A promise that resolves when all schedules are deleted
*/
export async function deleteAllSchedules(): Promise<void> {
await UserScheduleStore.set('schedules', []);
await UserScheduleStore.set('activeIndex', 0);
await createSchedule('Schedule 1');
}
2 changes: 2 additions & 0 deletions src/pages/calendar/CalendarMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import Calendar from '@views/components/calendar/Calendar';
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import { MessageListener } from 'chrome-extension-toolkit';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React, { useEffect } from 'react';

/**
* Calendar page
* @returns entire page
*/
export default function CalendarMain() {
useKC_DABR_WASM();
doprz marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
const tabInfoListener = new MessageListener<TabInfoMessages>({
getTabInfo: ({ sendResponse }) => {
Expand Down
22 changes: 22 additions & 0 deletions src/pages/options/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import Settings from '@views/components/settings/Settings';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React from 'react';

/**
* Renders the settings page for the UTRP (UT Registration Plus) extension.
* Allows customization options and displays credits for the development team.
*
* @returns The JSX element representing the settings page.
*/
export default function SettingsPage() {
useKC_DABR_WASM();
return (
<ExtensionRoot>
<DialogProvider>
<Settings />
</DialogProvider>
</ExtensionRoot>
);
}
4 changes: 2 additions & 2 deletions src/pages/options/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { createRoot } from 'react-dom/client';

import App from './App';
import SettingsPage from './Settings';

createRoot(document.getElementById('root')!).render(<App />);
createRoot(document.getElementById('root')!).render(<SettingsPage />);
38 changes: 31 additions & 7 deletions src/shared/storage/OptionsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,44 @@ import { createSyncStore, debugStore } from 'chrome-extension-toolkit';
/**
* A store that is used for storing user options
*/
interface IOptionsStore {
/** whether we should automatically highlight conflicts on the course schedule page */
shouldHighlightConflicts: boolean;
export interface IOptionsStore {
/** whether we should enable course status chips (indicator for waitlisted, cancelled, and closed courses) */
enableCourseStatusChips: boolean;

/** whether we should enable course's time and location in the extension's popup */
enableTimeAndLocationInPopup: boolean;
doprz marked this conversation as resolved.
Show resolved Hide resolved

/** whether we should automatically highlight conflicts on the course schedule page (adds a red strikethrough to courses that have conflicting times) */
enableHighlightConflicts: boolean;

/** whether we should automatically scroll to load more courses on the course schedule page (without having to click next) */
shouldScrollToLoad: boolean;
enableScrollToLoad: boolean;

// url: URL;
/** whether we should automatically refresh the data for the waitlist, course status, and other info with the latest data from UT's site */
enableDataRefreshing: boolean;
}

export const OptionsStore = createSyncStore<IOptionsStore>({
shouldHighlightConflicts: true,
shouldScrollToLoad: true,
enableCourseStatusChips: false,
enableTimeAndLocationInPopup: false,
enableHighlightConflicts: true,
enableScrollToLoad: true,
enableDataRefreshing: true,
});

/**
* Initializes the settings by retrieving the values from the OptionsStore.
* @returns {Promise<IOptionsStore>} A promise that resolves to an object satisfying the IOptionsStore interface.
*/
export const initSettings = async () =>
({
enableCourseStatusChips: await OptionsStore.get('enableCourseStatusChips'),
enableTimeAndLocationInPopup: await OptionsStore.get('enableTimeAndLocationInPopup'),
enableHighlightConflicts: await OptionsStore.get('enableHighlightConflicts'),
enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'),
enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'),
}) satisfies IOptionsStore;

// Clothing retailer right

debugStore({ OptionsStore });
3 changes: 0 additions & 3 deletions src/shared/util/experimental.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/stories/components/Settings.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react';
import Settings from '@views/components/Settings';
import Settings from '@views/components/settings/Settings';

const meta = {
title: 'Components/Common/Settings',
Expand Down
31 changes: 29 additions & 2 deletions src/views/components/PopupMain.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import splashText from '@assets/insideJokes';
import { background } from '@shared/messages';
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { enableCourseRefreshing, enableCourseStatusChips } from '@shared/util/experimental';
import Divider from '@views/components/common/Divider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import List from '@views/components/common/List';
Expand All @@ -10,6 +10,7 @@ import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import clsx from 'clsx';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React, { useEffect, useState } from 'react';

import CalendarIcon from '~icons/material-symbols/calendar-month';
Expand All @@ -28,6 +29,32 @@ import ScheduleListItem from './common/ScheduleListItem';
* This component displays the main schedule, courses, and options buttons.
*/
export default function PopupMain(): JSX.Element {
const [enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false);
const [enableDataRefreshing, setEnableDataRefreshing] = useState<boolean>(false);
useKC_DABR_WASM();

useEffect(() => {
initSettings().then(({ enableCourseStatusChips, enableDataRefreshing }) => {
setEnableCourseStatusChips(enableCourseStatusChips);
setEnableDataRefreshing(enableDataRefreshing);
});

const l1 = OptionsStore.listen('enableCourseStatusChips', async ({ newValue }) => {
setEnableCourseStatusChips(newValue);
// console.log('enableCourseStatusChips', newValue);
});

const l2 = OptionsStore.listen('enableDataRefreshing', async ({ newValue }) => {
setEnableDataRefreshing(newValue);
// console.log('enableDataRefreshing', newValue);
doprz marked this conversation as resolved.
Show resolved Hide resolved
});

return () => {
OptionsStore.removeListener(l1);
OptionsStore.removeListener(l2);
};
}, []);

const [activeSchedule, schedules] = useSchedules();
const [isRefreshing, setIsRefreshing] = useState(false);
const [funny, setFunny] = useState<string>('');
Expand Down Expand Up @@ -139,7 +166,7 @@ export default function PopupMain(): JSX.Element {
</>
)}
</div>
{enableCourseRefreshing && (
{enableDataRefreshing && (
<div className='inline-flex items-center self-center gap-1'>
<Text variant='mini' className='text-ut-gray !font-normal'>
DATA LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
Expand Down
15 changes: 0 additions & 15 deletions src/views/components/Settings.tsx

This file was deleted.

19 changes: 17 additions & 2 deletions src/views/components/calendar/CalendarCourseCell.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import type { StatusType } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type { CourseColors } from '@shared/types/ThemeColors';
import { pickFontColor } from '@shared/util/colors';
import { enableCourseStatusChips } from '@shared/util/experimental';
import Text from '@views/components/common/Text/Text';
import clsx from 'clsx';
import React from 'react';
import React, { useEffect, useState } from 'react';

import ClosedIcon from '~icons/material-symbols/lock';
import WaitlistIcon from '~icons/material-symbols/timelapse';
Expand Down Expand Up @@ -43,6 +43,21 @@ export default function CalendarCourseCell({
className,
onClick,
}: CalendarCourseCellProps): JSX.Element {
const [enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false);

useEffect(() => {
initSettings().then(({ enableCourseStatusChips }) => setEnableCourseStatusChips(enableCourseStatusChips));
doprz marked this conversation as resolved.
Show resolved Hide resolved

const l1 = OptionsStore.listen('enableCourseStatusChips', async ({ newValue }) => {
setEnableCourseStatusChips(newValue);
// console.log('enableCourseStatusChips', newValue);
doprz marked this conversation as resolved.
Show resolved Hide resolved
});

return () => {
OptionsStore.removeListener(l1);
};
}, []);

let rightIcon: React.ReactNode | null = null;
if (enableCourseStatusChips) {
if (status === Status.WAITLISTED) {
Expand Down
31 changes: 28 additions & 3 deletions src/views/components/calendar/CalenderHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import { Status } from '@shared/types/Course';
import { enableCourseRefreshing, enableCourseStatusChips } from '@shared/util/experimental';
import { Button } from '@views/components/common/Button';
import CourseStatus from '@views/components/common/CourseStatus';
import Divider from '@views/components/common/Divider';
Expand All @@ -9,7 +9,7 @@ import Text from '@views/components/common/Text/Text';
import useSchedules from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import React from 'react';
import React, { useEffect, useState } from 'react';

import MenuIcon from '~icons/material-symbols/menu';
import RefreshIcon from '~icons/material-symbols/refresh';
Expand All @@ -32,8 +32,33 @@ interface CalendarHeaderProps {
* @returns The JSX element representing the calendar header.
*/
export default function CalendarHeader({ onSidebarToggle }: CalendarHeaderProps): JSX.Element {
const [enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false);
const [enableDataRefreshing, setEnableDataRefreshing] = useState<boolean>(false);

const [activeSchedule] = useSchedules();

useEffect(() => {
initSettings().then(({ enableCourseStatusChips, enableDataRefreshing }) => {
setEnableCourseStatusChips(enableCourseStatusChips);
setEnableDataRefreshing(enableDataRefreshing);
});

const l1 = OptionsStore.listen('enableCourseStatusChips', async ({ newValue }) => {
setEnableCourseStatusChips(newValue);
// console.log('enableCourseStatusChips', newValue);
});

const l2 = OptionsStore.listen('enableDataRefreshing', async ({ newValue }) => {
setEnableDataRefreshing(newValue);
// console.log('enableDataRefreshing', newValue);
});

return () => {
OptionsStore.removeListener(l1);
OptionsStore.removeListener(l2);
};
}, []);

return (
<div className='flex items-center gap-5 overflow-x-auto overflow-y-hidden border-b border-ut-offwhite px-7 py-4 md:overflow-x-hidden'>
<Button
Expand All @@ -51,7 +76,7 @@ export default function CalendarHeader({ onSidebarToggle }: CalendarHeaderProps)
totalHours={activeSchedule.hours}
totalCourses={activeSchedule.courses.length}
/>
{enableCourseRefreshing && (
{enableDataRefreshing && (
<div className='flex items-center gap-1 screenshot:hidden'>
<Text variant='mini' className='text-nowrap text-ut-gray font-normal!'>
DATA LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
Expand Down
19 changes: 17 additions & 2 deletions src/views/components/common/PopupCourseBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd';
import { background } from '@shared/messages';
import { initSettings, OptionsStore } from '@shared/storage/OptionsStore';
import type { Course } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type { CourseColors } from '@shared/types/ThemeColors';
import { pickFontColor } from '@shared/util/colors';
import { enableCourseStatusChips } from '@shared/util/experimental';
import { StatusIcon } from '@shared/util/icons';
import Text from '@views/components/common/Text/Text';
import clsx from 'clsx';
import React from 'react';
import React, { useEffect, useState } from 'react';

import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';

Expand All @@ -33,6 +33,21 @@ export default function PopupCourseBlock({
colors,
dragHandleProps,
}: PopupCourseBlockProps): JSX.Element {
const [enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false);

useEffect(() => {
initSettings().then(({ enableCourseStatusChips }) => setEnableCourseStatusChips(enableCourseStatusChips));

const l1 = OptionsStore.listen('enableCourseStatusChips', async ({ newValue }) => {
setEnableCourseStatusChips(newValue);
// console.log('enableCourseStatusChips', newValue);
});

return () => {
OptionsStore.removeListener(l1);
};
}, []);

// text-white or text-black based on secondaryColor
const fontColor = pickFontColor(colors.primaryColor);
const formattedUniqueId = course.uniqueId.toString().padStart(5, '0');
Expand Down
2 changes: 1 addition & 1 deletion src/views/components/common/SwitchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const SwitchButton = ({ isChecked = true, onChange }: ToggleSwitchProps): JSX.El
checked={enabled}
onChange={handleChange}
className={`${enabled ? 'bg-[#579D42]' : 'bg-gray-400'}
relative inline-flex items-center h-8 w-13 rounded-full transition-colors ease-in-out duration-200`}
relative inline-flex items-center h-8 w-13 rounded-full transition-colors ease-in-out duration-200 min-w-[52px]`}
doprz marked this conversation as resolved.
Show resolved Hide resolved
>
<span
className={`${enabled ? 'translate-x-6' : 'translate-x-1'}
Expand Down
7 changes: 7 additions & 0 deletions src/views/components/injected/TableRow/TableRow.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,10 @@
text-decoration: line-through;
}
}

.isConflictNoLineThrough:not(.inActiveSchedule) {
> *:not(td:last-child) {
color: colors.$speedway_brick;
font-weight: normal;
}
}
Loading
Loading