refactor: Replace Webpack with Vite (#53)
This commit is contained in:
@@ -1,221 +0,0 @@
|
||||
[
|
||||
"ACC",
|
||||
"ADV",
|
||||
"ASE",
|
||||
"AFR",
|
||||
"AFS",
|
||||
"ASL",
|
||||
"AMS",
|
||||
"AHC",
|
||||
"ANT",
|
||||
"ALD",
|
||||
"ARA",
|
||||
"ARE",
|
||||
"ARI",
|
||||
"ARC",
|
||||
"AED",
|
||||
"ARH",
|
||||
"ART",
|
||||
"AET",
|
||||
"AAS",
|
||||
"ANS",
|
||||
"AST",
|
||||
"BSN",
|
||||
"BEN",
|
||||
"BCH",
|
||||
"BIO",
|
||||
"BME",
|
||||
"BDP",
|
||||
"B A",
|
||||
"BAX",
|
||||
"BGS",
|
||||
"CHE",
|
||||
"CH",
|
||||
"CHI",
|
||||
"C E",
|
||||
"CLA",
|
||||
"C C",
|
||||
"CGS",
|
||||
"COM",
|
||||
"CLD",
|
||||
"CMS",
|
||||
"CRP",
|
||||
"C L",
|
||||
"COE",
|
||||
"CSE",
|
||||
"C S",
|
||||
"CON",
|
||||
"CTI",
|
||||
"CRW",
|
||||
"CDI",
|
||||
"EDC",
|
||||
"CZ",
|
||||
"DAN",
|
||||
"DSC",
|
||||
"D S",
|
||||
"DES",
|
||||
"DEV",
|
||||
"D B",
|
||||
"DRS",
|
||||
"DCH",
|
||||
"ECO",
|
||||
"ELP",
|
||||
"EDP",
|
||||
"E E",
|
||||
"ECE",
|
||||
"EER",
|
||||
"EMA",
|
||||
"ENM",
|
||||
"E M",
|
||||
"E S",
|
||||
"E",
|
||||
"ESL",
|
||||
"ENS",
|
||||
"EVE",
|
||||
"EVS",
|
||||
"EUP",
|
||||
"EUS",
|
||||
"FIN",
|
||||
"F A",
|
||||
"FLU",
|
||||
"FR",
|
||||
"F H",
|
||||
"G E",
|
||||
"GRG",
|
||||
"GEO",
|
||||
"GER",
|
||||
"GSD",
|
||||
"GOV",
|
||||
"GRS",
|
||||
"GK",
|
||||
"GUI",
|
||||
"HAR",
|
||||
"H S",
|
||||
"HCT",
|
||||
"HED",
|
||||
"HEB",
|
||||
"HIN",
|
||||
"HIS",
|
||||
"HDF",
|
||||
"HDO",
|
||||
"H E",
|
||||
"HMN",
|
||||
"ILA",
|
||||
"I",
|
||||
"ISP",
|
||||
"INF",
|
||||
"ITD",
|
||||
"I B",
|
||||
"IRG",
|
||||
"ISL",
|
||||
"ITL",
|
||||
"ITC",
|
||||
"JPN",
|
||||
"J S",
|
||||
"J",
|
||||
"KIN",
|
||||
"KOR",
|
||||
"LAR",
|
||||
"LTC",
|
||||
"LAT",
|
||||
"LAL",
|
||||
"LAS",
|
||||
"LAW",
|
||||
"LEB",
|
||||
"L A",
|
||||
"LAH",
|
||||
"LIN",
|
||||
"MAL",
|
||||
"MAN",
|
||||
"MIS",
|
||||
"MFG",
|
||||
"MNS",
|
||||
"MKT",
|
||||
"MSE",
|
||||
"M",
|
||||
"M E",
|
||||
"MDV",
|
||||
"MAS",
|
||||
"MEL",
|
||||
"MES",
|
||||
"M S",
|
||||
"MOL",
|
||||
"MUS",
|
||||
"NSC",
|
||||
"N S",
|
||||
"NEU",
|
||||
"NOR",
|
||||
"N",
|
||||
"NTR",
|
||||
"OBO",
|
||||
"OPR",
|
||||
"O M",
|
||||
"ORI",
|
||||
"ORG",
|
||||
"PER",
|
||||
"PRS",
|
||||
"PGE",
|
||||
"PGS",
|
||||
"PHM",
|
||||
"PHL",
|
||||
"PED",
|
||||
"P S",
|
||||
"PHY",
|
||||
"PIA",
|
||||
"POL",
|
||||
"POR",
|
||||
"PRC",
|
||||
"PSY",
|
||||
"P A",
|
||||
"PBH",
|
||||
"P R",
|
||||
"RIM",
|
||||
"RTF",
|
||||
"R E",
|
||||
"R S",
|
||||
"RHE",
|
||||
"R M",
|
||||
"RUS",
|
||||
"REE",
|
||||
"SAN",
|
||||
"SAX",
|
||||
"STC",
|
||||
"STM",
|
||||
"S C",
|
||||
"SEL",
|
||||
"S S",
|
||||
"S W",
|
||||
"SOC",
|
||||
"SPN",
|
||||
"SPC",
|
||||
"SED",
|
||||
"SLH",
|
||||
"STA",
|
||||
"SDS",
|
||||
"SUS",
|
||||
"SWE",
|
||||
"TAM",
|
||||
"TXA",
|
||||
"T D",
|
||||
"TRO",
|
||||
"TRU",
|
||||
"TBA",
|
||||
"TUR",
|
||||
"T C",
|
||||
"UKR",
|
||||
"UGS",
|
||||
"UDN",
|
||||
"URB",
|
||||
"URD",
|
||||
"UTS",
|
||||
"UTL",
|
||||
"VIA",
|
||||
"VIO",
|
||||
"V C",
|
||||
"VAS",
|
||||
"VOI",
|
||||
"WGS",
|
||||
"WRT",
|
||||
"YID",
|
||||
"YOR"
|
||||
]
|
||||
3
src/assets/robots.txt
Normal file
3
src/assets/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,33 +0,0 @@
|
||||
import io from 'socket.io-client';
|
||||
import { background } from 'src/shared/messages';
|
||||
|
||||
const socket = io('http://localhost:9090');
|
||||
let reBuilding = false;
|
||||
|
||||
socket.on('disconnect', async reason => {
|
||||
reBuilding = reason.includes('transport') && !reason.includes('client');
|
||||
});
|
||||
|
||||
socket.onAny(args => {
|
||||
console.log(args);
|
||||
});
|
||||
|
||||
socket.on('connect', async () => {
|
||||
if (!reBuilding) {
|
||||
console.log('%c[hot-reloading] listening for changes...', 'color:white; background-color: orange;');
|
||||
} else {
|
||||
console.log(
|
||||
'%c[hot-reloading] changes detected, rebuilding and refreshing...',
|
||||
'color:white; background-color: orange;'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('reload', async () => {
|
||||
console.log('%c[hot-reloading] reloading...', 'color:white; background-color: orange;');
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
|
||||
if (tabs?.[0]?.id) {
|
||||
background.reloadExtension();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
import './hotReload';
|
||||
import { DevStore } from '@shared/storage/DevStore';
|
||||
import React, { useEffect } from 'react';
|
||||
import { DevStore } from 'src/shared/storage/DevStore';
|
||||
import render from 'src/views/lib/react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
|
||||
@@ -146,4 +145,4 @@ function DevDashboard() {
|
||||
);
|
||||
}
|
||||
|
||||
render(<DevDashboard />, document.getElementById('root'));
|
||||
createRoot(document.getElementById('root')).render(<DevDashboard />);
|
||||
|
||||
14
src/global.d.ts
vendored
Normal file
14
src/global.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
declare module '*.jpg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.json' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
54
src/manifest.ts
Normal file
54
src/manifest.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { defineManifest } from '@crxjs/vite-plugin';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
// Convert from Semver (example: 0.1.0-beta6)
|
||||
const [major, minor, patch, label = '0'] = packageJson.version
|
||||
// can only contain digits, dots, or dash
|
||||
.replace(/[^\d.-]+/g, '')
|
||||
// split into version parts
|
||||
.split(/[.-]/);
|
||||
|
||||
const mode = process.env.NODE_ENV;
|
||||
|
||||
const HOST_PERMISSIONS: string[] = [
|
||||
'*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*',
|
||||
'*://*.utexas.collegescheduler.com/*',
|
||||
'*://*.catalog.utexas.edu/ribbit/',
|
||||
'*://*.registrar.utexas.edu/schedules/*',
|
||||
'*://*.login.utexas.edu/login/*',
|
||||
];
|
||||
|
||||
const manifest = defineManifest(async () => ({
|
||||
manifest_version: 3,
|
||||
name: `${packageJson.displayName ?? packageJson.name}${mode === 'development' ? ' (dev)' : ''}`,
|
||||
version: `${major}.${minor}.${patch}.${label}`,
|
||||
description: packageJson.description,
|
||||
options_page: 'src/pages/options/index.html',
|
||||
background: { service_worker: 'src/pages/background/background.ts' },
|
||||
permissions: ['storage', 'unlimitedStorage', 'background'],
|
||||
host_permissions: process.env.MODE === 'development' ? [...HOST_PERMISSIONS, '<all_urls>'] : HOST_PERMISSIONS,
|
||||
action: {
|
||||
default_popup: 'src/pages/popup/index.html',
|
||||
default_icon: `icons/icon_${mode}_32.png`,
|
||||
},
|
||||
icons: {
|
||||
'16': `icons/icon_${mode}_16.png`,
|
||||
'32': `icons/icon_${mode}_32.png`,
|
||||
'48': `icons/icon_${mode}_48.png`,
|
||||
'128': `icons/icon_${mode}_128.png`,
|
||||
},
|
||||
content_scripts: [
|
||||
{
|
||||
matches: HOST_PERMISSIONS,
|
||||
js: ['src/pages/content/index.tsx'],
|
||||
},
|
||||
],
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ['assets/js/*.js', 'assets/css/*.css', 'assets/img/*'],
|
||||
matches: ['*://*/*'],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
export default manifest;
|
||||
@@ -1,10 +1,9 @@
|
||||
import { BACKGROUND_MESSAGES } from '@shared/messages';
|
||||
import { MessageListener } from 'chrome-extension-toolkit';
|
||||
import { BACKGROUND_MESSAGES } from 'src/shared/messages';
|
||||
import onInstall from './events/onInstall';
|
||||
import onServiceWorkerAlive from './events/onServiceWorkerAlive';
|
||||
import onUpdate from './events/onUpdate';
|
||||
import browserActionHandler from './handler/browserActionHandler';
|
||||
import hotReloadingHandler from './handler/hotReloadingHandler';
|
||||
import tabManagementHandler from './handler/tabManagementHandler';
|
||||
import userScheduleHandler from './handler/userScheduleHandler';
|
||||
|
||||
@@ -30,7 +29,6 @@ chrome.runtime.onInstalled.addListener(details => {
|
||||
// initialize the message listener that will listen for messages from the content script
|
||||
const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
|
||||
...browserActionHandler,
|
||||
...hotReloadingHandler,
|
||||
...tabManagementHandler,
|
||||
...userScheduleHandler,
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExtensionStore } from '../../shared/storage/ExtensionStore';
|
||||
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
||||
|
||||
/**
|
||||
* Called when the extension is first installed or synced onto a new machine
|
||||
@@ -1,5 +1,4 @@
|
||||
import { hotReloadTab } from 'src/background/util/hotReloadTab';
|
||||
import { ExtensionStore } from '../../shared/storage/ExtensionStore';
|
||||
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
||||
|
||||
/**
|
||||
* Called when the extension is updated (or when the extension is reloaded in development mode)
|
||||
@@ -9,8 +8,4 @@ export default async function onUpdate() {
|
||||
version: chrome.runtime.getManifest().version,
|
||||
lastUpdate: Date.now(),
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
hotReloadTab();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import BrowserActionMessages from '@shared/messages/BrowserActionMessages';
|
||||
import { MessageHandler } from 'chrome-extension-toolkit';
|
||||
import BrowserActionMessages from 'src/shared/messages/BrowserActionMessages';
|
||||
|
||||
const browserActionHandler: MessageHandler<BrowserActionMessages> = {
|
||||
disableBrowserAction({ sender, sendResponse }) {
|
||||
@@ -1,6 +1,6 @@
|
||||
import HotReloadingMessages from 'src/shared/messages/HotReloadingMessages';
|
||||
import HotReloadingMessages from '@shared/messages/HotReloadingMessages';
|
||||
import { DevStore } from '@shared/storage/DevStore';
|
||||
import { MessageHandler } from 'chrome-extension-toolkit';
|
||||
import { DevStore } from 'src/shared/storage/DevStore';
|
||||
|
||||
const hotReloadingHandler: MessageHandler<HotReloadingMessages> = {
|
||||
async reloadExtension({ sendResponse }) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import TabManagementMessages from '@shared/messages/TabManagementMessages';
|
||||
import { MessageHandler } from 'chrome-extension-toolkit';
|
||||
import TabManagementMessages from 'src/shared/messages/TabManagementMessages';
|
||||
import openNewTab from '../util/openNewTab';
|
||||
|
||||
const tabManagementHandler: MessageHandler<TabManagementMessages> = {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UserScheduleMessages } from '@shared/messages/UserScheduleMessages';
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { MessageHandler } from 'chrome-extension-toolkit';
|
||||
import { UserScheduleMessages } from 'src/shared/messages/UserScheduleMessages';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import addCourse from '../lib/addCourse';
|
||||
import clearCourses from '../lib/clearCourses';
|
||||
import createSchedule from '../lib/createSchedule';
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
import { Course } from '@shared/types/Course';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
|
||||
export default async function clearCourses(scheduleName: string): Promise<void> {
|
||||
const schedules = await UserScheduleStore.get('schedules');
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
|
||||
/**
|
||||
* Creates a new schedule with the given name
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
|
||||
export default async function deleteSchedule(scheduleName: string): Promise<string | undefined> {
|
||||
const [schedules, activeIndex] = await Promise.all([
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
import { Course } from '@shared/types/Course';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
|
||||
export default async function renameSchedule(scheduleName: string, newName: string): Promise<string | undefined> {
|
||||
const schedules = await UserScheduleStore.get('schedules');
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
|
||||
export default async function switchSchedule(scheduleName: string): Promise<void> {
|
||||
const schedules = await UserScheduleStore.get('schedules');
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DevStore } from 'src/shared/storage/DevStore';
|
||||
import { DevStore } from '@shared/storage/DevStore';
|
||||
|
||||
/**
|
||||
* A list of websites that we don't want to reload when the extension reloads (becuase it'd be hella annoying lmao)
|
||||
@@ -40,5 +40,3 @@ export async function hotReloadTab(): Promise<void> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DevStore } from 'src/shared/storage/DevStore';
|
||||
import { DevStore } from '@shared/storage/DevStore';
|
||||
|
||||
/**
|
||||
* Open the debug tab as the first tab
|
||||
10
src/pages/calendar/CalendarMain.tsx
Normal file
10
src/pages/calendar/CalendarMain.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||
|
||||
export default function CalendarMain() {
|
||||
return (
|
||||
<ExtensionRoot>
|
||||
<div>Calendar Placeholder</div>
|
||||
</ExtensionRoot>
|
||||
);
|
||||
}
|
||||
18
src/pages/calendar/index.html
Normal file
18
src/pages/calendar/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>Calendar</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
5
src/pages/calendar/index.tsx
Normal file
5
src/pages/calendar/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import CalendarMain from './CalendarMain';
|
||||
|
||||
createRoot(document.getElementById('root')).render(<CalendarMain />);
|
||||
18
src/pages/content/index.tsx
Normal file
18
src/pages/content/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import CourseCatalogMain from '@views/components/CourseCatalogMain';
|
||||
import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
|
||||
const support = getSiteSupport(window.location.href);
|
||||
|
||||
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
|
||||
const container = document.createElement('div');
|
||||
container.id = 'extension-root';
|
||||
document.body.appendChild(container);
|
||||
|
||||
createRoot(container).render(
|
||||
<React.StrictMode>
|
||||
<CourseCatalogMain support={support} />
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
13
src/pages/debug/App.tsx
Normal file
13
src/pages/debug/App.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function App() {
|
||||
return (
|
||||
<ExtensionRoot>
|
||||
<div>hello how are you doing today.</div>
|
||||
</ExtensionRoot>
|
||||
);
|
||||
}
|
||||
18
src/pages/debug/index.html
Normal file
18
src/pages/debug/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>Debug</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1
src/pages/debug/index.tsx
Normal file
1
src/pages/debug/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
import 'src/debug';
|
||||
13
src/pages/options/App.tsx
Normal file
13
src/pages/options/App.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function App() {
|
||||
return (
|
||||
<ExtensionRoot>
|
||||
<div>hello how are you doing today.</div>
|
||||
</ExtensionRoot>
|
||||
);
|
||||
}
|
||||
18
src/pages/options/index.html
Normal file
18
src/pages/options/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
5
src/pages/options/index.tsx
Normal file
5
src/pages/options/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
createRoot(document.getElementById('root')).render(<App />);
|
||||
0
src/pages/panel/index.tsx
Normal file
0
src/pages/panel/index.tsx
Normal file
18
src/pages/popup/index.html
Normal file
18
src/pages/popup/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
5
src/pages/popup/index.tsx
Normal file
5
src/pages/popup/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import PopupMain from '../../views/components/PopupMain';
|
||||
|
||||
createRoot(document.getElementById('root')).render(<PopupMain />);
|
||||
@@ -1,17 +1,13 @@
|
||||
import { createMessenger } from 'chrome-extension-toolkit';
|
||||
import TAB_MESSAGES from './TabMessages';
|
||||
import BrowserActionMessages from './BrowserActionMessages';
|
||||
import HotReloadingMessages from './HotReloadingMessages';
|
||||
import TabManagementMessages from './TabManagementMessages';
|
||||
import TAB_MESSAGES from './TabMessages';
|
||||
import { UserScheduleMessages } from './UserScheduleMessages';
|
||||
|
||||
/**
|
||||
* This is a type with all the message definitions that can be sent TO the background script
|
||||
*/
|
||||
export type BACKGROUND_MESSAGES = BrowserActionMessages &
|
||||
TabManagementMessages &
|
||||
HotReloadingMessages &
|
||||
UserScheduleMessages;
|
||||
export type BACKGROUND_MESSAGES = BrowserActionMessages & TabManagementMessages & UserScheduleMessages;
|
||||
|
||||
/**
|
||||
* A utility object that can be used to send type-safe messages to the background script
|
||||
|
||||
@@ -24,10 +24,4 @@ export const DevStore = createLocalStore<IDevStore>({
|
||||
reloadTabId: undefined,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
debugStore({ devStore: DevStore });
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IOptionsStore {
|
||||
/** whether we should automatically scroll to load more courses on the course schedule page (without having to click next) */
|
||||
shouldScrollToLoad: boolean;
|
||||
|
||||
url: URL;
|
||||
// url: URL;
|
||||
}
|
||||
|
||||
export const OptionsStore = createSyncStore<IOptionsStore>({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import { createLocalStore, debugStore } from 'chrome-extension-toolkit';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
|
||||
interface IUserScheduleStore {
|
||||
schedules: UserSchedule[];
|
||||
|
||||
@@ -11,7 +11,4 @@
|
||||
"node"
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"../webpack"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Course, ScrapedRow } from 'src/shared/types/Course';
|
||||
import { useKeyPress } from '../hooks/useKeyPress';
|
||||
import useSchedules from '../hooks/useSchedules';
|
||||
import { CourseCatalogScraper } from '../lib/CourseCatalogScraper';
|
||||
@@ -7,13 +7,12 @@ import getCourseTableRows from '../lib/getCourseTableRows';
|
||||
import { SiteSupport } from '../lib/getSiteSupport';
|
||||
import { populateSearchInputs } from '../lib/populateSearchInputs';
|
||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||
import Icon from './common/Icon/Icon';
|
||||
import Text from './common/Text/Text';
|
||||
import AutoLoad from './injected/AutoLoad/AutoLoad';
|
||||
import CoursePopup from './injected/CoursePopup/CoursePopup';
|
||||
import RecruitmentBanner from './injected/RecruitmentBanner/RecruitmentBanner';
|
||||
import TableHead from './injected/TableHead';
|
||||
import TableRow from './injected/TableRow/TableRow';
|
||||
import TableSubheading from './injected/TableSubheading/TableSubheading';
|
||||
|
||||
interface Props {
|
||||
support: SiteSupport.COURSE_CATALOG_DETAILS | SiteSupport.COURSE_CATALOG_LIST;
|
||||
@@ -33,7 +32,7 @@ export default function CourseCatalogMain({ support }: Props) {
|
||||
useEffect(() => {
|
||||
const tableRows = getCourseTableRows(document);
|
||||
const ccs = new CourseCatalogScraper(support);
|
||||
const scrapedRows = ccs.scrape(tableRows);
|
||||
const scrapedRows = ccs.scrape(tableRows, true);
|
||||
setRows(scrapedRows);
|
||||
}, [support]);
|
||||
|
||||
@@ -64,10 +63,10 @@ export default function CourseCatalogMain({ support }: Props) {
|
||||
<ExtensionRoot>
|
||||
<RecruitmentBanner />
|
||||
<TableHead>Plus</TableHead>
|
||||
{rows.map(row => {
|
||||
{rows.map((row, i) => {
|
||||
if (!row.course) {
|
||||
// TODO: handle the course section headers
|
||||
return null;
|
||||
return <TableSubheading key={row.element.innerText + i.toString()} row={row} />;
|
||||
}
|
||||
return (
|
||||
<TableRow
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||
|
||||
export default function MyCalendarMain() {
|
||||
return <ExtensionRoot>MyCalendarMain</ExtensionRoot>;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { background } from '@shared/messages';
|
||||
import React from 'react';
|
||||
import { background } from 'src/shared/messages';
|
||||
import useSchedules from '../hooks/useSchedules';
|
||||
import { Button } from './common/Button/Button';
|
||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'sass:color';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.button {
|
||||
background-color: #000;
|
||||
@@ -7,9 +8,8 @@
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
box-shadow: rgba(0, 0, 0, 0.4) 2px 2px 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: 'Inter';
|
||||
|
||||
display: flex;
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
&:active {
|
||||
animation: click_animation 0.2s ease-in-out;
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@@ -30,20 +30,20 @@
|
||||
opacity: 0.5 !important;
|
||||
|
||||
&:active {
|
||||
animation: none !important;
|
||||
transform: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@each $color,
|
||||
$value
|
||||
in (
|
||||
primary: $burnt_orange,
|
||||
secondary: $charcoal,
|
||||
tertiary: $bluebonnet,
|
||||
danger: $speedway_brick,
|
||||
warning: $tangerine,
|
||||
success: $turtle_pond,
|
||||
info: $turquoise
|
||||
primary: colors.$burnt_orange,
|
||||
secondary: colors.$charcoal,
|
||||
tertiary: colors.$bluebonnet,
|
||||
danger: colors.$speedway_brick,
|
||||
warning: colors.$tangerine,
|
||||
success: colors.$turtle_pond,
|
||||
info: colors.$turquoise
|
||||
)
|
||||
{
|
||||
&.#{$color} {
|
||||
@@ -51,12 +51,12 @@
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($value, 10%);
|
||||
background-color: color.adjust($value, $lightness: 10%);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-visible,
|
||||
&:active {
|
||||
background-color: darken($value, 10%);
|
||||
background-color: color.adjust($value, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@@ -65,15 +65,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes click_animation {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.card {
|
||||
background: $white;
|
||||
background: colors.$white;
|
||||
border: 0.5px dotted #c3cee0;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
hr {
|
||||
border: 1px solid $limestone;
|
||||
border: 1px solid colors.$limestone;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Color } from 'src/views/styles/colors.module.scss';
|
||||
import React from 'react';
|
||||
import { Color } from '@views/styles/colors.module.scss';
|
||||
import styles from './Divider.module.scss';
|
||||
|
||||
export type Props = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@import 'src/views/styles/fonts.module.scss';
|
||||
@use 'src/views/styles/fonts.module.scss';
|
||||
|
||||
.icon {
|
||||
font-family: 'Material Icons Round';
|
||||
font-weight: $normal_weight;
|
||||
font-weight: fonts.$normal_weight;
|
||||
font-style: normal;
|
||||
font-size: $medium_size;
|
||||
font-size: fonts.$medium_size;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import colors, { Color } from 'src/views/styles/colors.module.scss';
|
||||
import fonts, { Size, Weight } from 'src/views/styles/fonts.module.scss';
|
||||
import colors, { Color } from '@views/styles/colors.module.scss';
|
||||
import fonts, { Size } from '@views/styles/fonts.module.scss';
|
||||
import styles from './Icon.module.scss';
|
||||
import { MaterialIconCode } from './MaterialIcons';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export type Props = {
|
||||
name: MaterialIconCode;
|
||||
className?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { background } from '@shared/messages';
|
||||
import classNames from 'classnames';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { background } from 'src/shared/messages';
|
||||
import Text, { TextProps } from '../Text/Text';
|
||||
import styles from './Link.module.scss';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
@@ -18,7 +18,7 @@
|
||||
.body {
|
||||
overflow-y: auto;
|
||||
z-index: 2147483647;
|
||||
background-color: $white;
|
||||
background-color: colors.$white;
|
||||
box-shadow: 0px 12px 30px 0px #323e5f29;
|
||||
transition: box-shadow 0.15s;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
$spinner-border-width: 10px;
|
||||
|
||||
.spinner {
|
||||
border: 1px solid $charcoal;
|
||||
border: 1px solid colors.$charcoal;
|
||||
border-width: $spinner-border-width;
|
||||
border-top: $spinner-border-width solid $tangerine;
|
||||
border-top: $spinner-border-width solid colors.$tangerine;
|
||||
margin: 0 auto 15px auto;
|
||||
border-radius: 50%;
|
||||
width: 60px;
|
||||
|
||||
@@ -1,59 +1,60 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
@use 'src/views/styles/fonts.module.scss';
|
||||
|
||||
.text {
|
||||
font-family: 'Inter', sans-serif;
|
||||
color: $charcoal;
|
||||
color: colors.$charcoal;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.light_weight {
|
||||
font-weight: $light_weight;
|
||||
font-weight: fonts.$light_weight;
|
||||
}
|
||||
|
||||
.regular_weight {
|
||||
font-weight: $regular_weight;
|
||||
font-weight: fonts.$regular_weight;
|
||||
}
|
||||
|
||||
.normal_weight {
|
||||
font-weight: $normal_weight;
|
||||
font-weight: fonts.$normal_weight;
|
||||
}
|
||||
|
||||
.semi_bold_weight {
|
||||
font-weight: $semi_bold_weight;
|
||||
font-weight: fonts.$semi_bold_weight;
|
||||
}
|
||||
|
||||
.bold_weight {
|
||||
font-weight: $bold_weight;
|
||||
font-weight: fonts.$bold_weight;
|
||||
}
|
||||
|
||||
.black_weight {
|
||||
font-weight: $black_weight;
|
||||
font-weight: fonts.$black_weight;
|
||||
}
|
||||
|
||||
.x_small_size {
|
||||
font-size: $x_small_size;
|
||||
font-size: fonts.$x_small_size;
|
||||
}
|
||||
|
||||
.xx_small_size {
|
||||
font-size: $xx_small_size;
|
||||
font-size: fonts.$xx_small_size;
|
||||
}
|
||||
|
||||
.small_size {
|
||||
font-size: $small_size;
|
||||
font-size: fonts.$small_size;
|
||||
}
|
||||
|
||||
.medium_size {
|
||||
font-size: $medium_size;
|
||||
font-size: fonts.$medium_size;
|
||||
}
|
||||
|
||||
.large_size {
|
||||
font-size: $large_size;
|
||||
font-size: fonts.$large_size;
|
||||
}
|
||||
|
||||
.x_large_size {
|
||||
font-size: $x_large_size;
|
||||
font-size: fonts.$x_large_size;
|
||||
}
|
||||
|
||||
.xx_large_size {
|
||||
font-size: $xx_large_size;
|
||||
font-size: fonts.$xx_large_size;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import colors, { Color } from 'src/views/styles/colors.module.scss';
|
||||
import fonts, { Size, Weight } from 'src/views/styles/fonts.module.scss';
|
||||
import colors, { Color } from '@views/styles/colors.module.scss';
|
||||
import { Size, Weight } from '@views/styles/fonts.module.scss';
|
||||
import styles from './Text.module.scss';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export type TextProps = {
|
||||
color?: Color;
|
||||
weight?: Weight;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { ScrapedRow } from '@shared/types/Course';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { ScrapedRow } from 'src/shared/types/Course';
|
||||
import useInfiniteScroll from 'src/views/hooks/useInfiniteScroll';
|
||||
import { CourseCatalogScraper } from 'src/views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from 'src/views/lib/getSiteSupport';
|
||||
import useInfiniteScroll from '@views/hooks/useInfiniteScroll';
|
||||
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
import {
|
||||
loadNextCourseCatalogPage,
|
||||
AutoLoadStatus,
|
||||
loadNextCourseCatalogPage,
|
||||
removePaginationButtons,
|
||||
} from 'src/views/lib/loadNextCourseCatalogPage';
|
||||
import Spinner from '../../common/Spinner/Spinner';
|
||||
} from '@views/lib/loadNextCourseCatalogPage';
|
||||
import styles from './AutoLoad.module.scss';
|
||||
|
||||
type Props = {
|
||||
@@ -53,16 +52,21 @@ export default function AutoLoad({ addRows }: Props) {
|
||||
addRows(scrapedRows);
|
||||
}, [addRows]);
|
||||
|
||||
if (!container || status === AutoLoadStatus.IDLE) {
|
||||
if (!container || status === AutoLoadStatus.DONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<div>
|
||||
{status === AutoLoadStatus.LOADING && (
|
||||
<div>
|
||||
<Spinner />
|
||||
<h2>Loading Next Page...</h2>
|
||||
{status !== AutoLoadStatus.ERROR && (
|
||||
<div
|
||||
style={{
|
||||
height: '500px',
|
||||
backgroundColor: '#f4f4f4',
|
||||
}}
|
||||
>
|
||||
{/* <Spinner />
|
||||
<h2>Loading Next Page...</h2> */}
|
||||
</div>
|
||||
)}
|
||||
{status === AutoLoadStatus.ERROR && (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.container {
|
||||
margin: 20px;
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
.restriction {
|
||||
color: $speedway_brick;
|
||||
color: colors.$speedway_brick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import classNames from 'classnames';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import Spinner from 'src/views/components/common/Spinner/Spinner';
|
||||
import Text from 'src/views/components/common/Text/Text';
|
||||
import { CourseCatalogScraper } from 'src/views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from 'src/views/lib/getSiteSupport';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
import Card from '../../../common/Card/Card';
|
||||
import styles from './CourseDescription.module.scss';
|
||||
|
||||
@@ -18,6 +18,9 @@ enum LoadStatus {
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function CourseDescription({ course }: Props) {
|
||||
const [description, setDescription] = useState<string[]>([]);
|
||||
const [status, setStatus] = useState<LoadStatus>(LoadStatus.LOADING);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { background } from '@shared/messages';
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React from 'react';
|
||||
import { background } from 'src/shared/messages';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
import { Button } from 'src/views/components/common/Button/Button';
|
||||
import Card from 'src/views/components/common/Card/Card';
|
||||
import Icon from 'src/views/components/common/Icon/Icon';
|
||||
import Text from 'src/views/components/common/Text/Text';
|
||||
import { Button } from '@views/components/common/Button/Button';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import styles from './CourseButtons.module.scss';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
import Card from 'src/views/components/common/Card/Card';
|
||||
import Icon from 'src/views/components/common/Icon/Icon';
|
||||
import Link from 'src/views/components/common/Link/Link';
|
||||
import Text from 'src/views/components/common/Text/Text';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Link from '@views/components/common/Link/Link';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import CourseButtons from './CourseButtons/CourseButtons';
|
||||
import styles from './CourseHeader.module.scss';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
import Popup from '../../common/Popup/Popup';
|
||||
import CourseDescription from './CourseDescription/CourseDescription';
|
||||
import CourseHeader from './CourseHeader/CourseHeader';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
@use 'src/views/styles/elevation.module.scss';
|
||||
|
||||
.chartContainer {
|
||||
height: 250px;
|
||||
@@ -14,11 +15,11 @@
|
||||
justify-content: center;
|
||||
|
||||
select {
|
||||
z-index: $MAX_Z_INDEX;
|
||||
z-index: elevation.$MAX_Z_INDEX;
|
||||
padding: 4px;
|
||||
font-family: 'Inter';
|
||||
border-radius: 8px;
|
||||
border-color: $charcoal;
|
||||
border-color: colors.$charcoal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import { Course, Semester } from '@shared/types/Course';
|
||||
import { Distribution, LetterGrade } from '@shared/types/Distribution';
|
||||
import Highcharts from 'highcharts';
|
||||
import Card from 'src/views/components/common/Card/Card';
|
||||
import { Course, Semester } from 'src/shared/types/Course';
|
||||
import colors from 'src/views/styles/colors.module.scss';
|
||||
import Spinner from 'src/views/components/common/Spinner/Spinner';
|
||||
import Text from 'src/views/components/common/Text/Text';
|
||||
import Icon from 'src/views/components/common/Icon/Icon';
|
||||
import { Distribution, LetterGrade } from 'src/shared/types/Distribution';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import {
|
||||
NoDataError,
|
||||
queryAggregateDistribution,
|
||||
querySemesterDistribution,
|
||||
} from 'src/views/lib/database/queryDistribution';
|
||||
} from '@views/lib/database/queryDistribution';
|
||||
import colors from '@views/styles/colors.module.scss';
|
||||
import styles from './GradeDistribution.module.scss';
|
||||
|
||||
enum DataStatus {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.container {
|
||||
background-color: $burnt_orange;
|
||||
background-color: colors.$burnt_orange;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
|
||||
@@ -15,6 +15,9 @@ export default function TableHead({ children }: PropsWithChildren) {
|
||||
const lastTableHeadCell = document.querySelector('table thead th:last-child');
|
||||
lastTableHeadCell!.after(container);
|
||||
setContainer(container);
|
||||
return () => {
|
||||
container.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!container) {
|
||||
|
||||
@@ -1,33 +1,21 @@
|
||||
@import 'src/views/styles/base.module.scss';
|
||||
|
||||
.rowButton {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.selectedRow {
|
||||
* {
|
||||
background: $burnt_orange !important;
|
||||
color: white !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.inActiveSchedule {
|
||||
* {
|
||||
color: $turtle_pond !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
|
||||
.isConflict {
|
||||
* {
|
||||
color: $speedway_brick !important;
|
||||
font-weight: normal !important;
|
||||
text-decoration: line-through !important;
|
||||
}
|
||||
}
|
||||
@use 'src/views/styles/colors.module.scss';
|
||||
|
||||
.row {
|
||||
> td:first-child {
|
||||
padding-left: 12px !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
> td:last-child {
|
||||
padding-right: 12px !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
> * {
|
||||
transition: background-color 0.1s ease-in-out;
|
||||
}
|
||||
.conflictTooltip {
|
||||
position: relative;
|
||||
display: none;
|
||||
@@ -54,4 +42,46 @@
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
// :global(ul.flag) li {
|
||||
// transform: scale(1.5); // omg the flags are on ONE LONG GIF FILE AND SHIFTED BY Y COORDINATES
|
||||
// }
|
||||
}
|
||||
|
||||
.rowButton {
|
||||
margin: 0.25rem 0;
|
||||
&:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
place-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.selectedRow {
|
||||
> * {
|
||||
background: colors.$burnt_orange !important;
|
||||
color: white !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.rowButton {
|
||||
background: colors.$burnt_orange !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.inActiveSchedule {
|
||||
* {
|
||||
color: colors.$turtle_pond !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
|
||||
.isConflict {
|
||||
* {
|
||||
color: colors.$speedway_brick !important;
|
||||
font-weight: normal !important;
|
||||
text-decoration: line-through !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Course, ScrapedRow } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import Icon from '../../common/Icon/Icon';
|
||||
import Text from '../../common/Text/Text';
|
||||
@@ -29,6 +29,7 @@ export default function TableRow({ row, isSelected, activeSchedule, onClick }: P
|
||||
useEffect(() => {
|
||||
element.classList.add(styles.row);
|
||||
const portalContainer = document.createElement('td');
|
||||
portalContainer.style.textAlign = 'right';
|
||||
const lastTableCell = element.querySelector('td:last-child');
|
||||
lastTableCell!.after(portalContainer);
|
||||
setContainer(portalContainer);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@use 'src/views/styles/fonts.module.scss';
|
||||
|
||||
.subheader {
|
||||
h2 {
|
||||
font-size: fonts.$medium_size;
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ScrapedRow } from '@shared/types/Course';
|
||||
import { useEffect } from 'react';
|
||||
import styles from './TableSubheading.module.scss';
|
||||
|
||||
interface Props {
|
||||
row: ScrapedRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is injected into each row of the course catalog table.
|
||||
* @returns a react portal to the new td in the column or null if the column has not been created yet.
|
||||
*/
|
||||
export default function TableSubheading({ row }: Props) {
|
||||
const { element } = row;
|
||||
|
||||
useEffect(() => {
|
||||
element.classList.add(styles.subheader);
|
||||
|
||||
return () => {
|
||||
element.classList.remove(styles.subheader);
|
||||
};
|
||||
}, [element]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import { useEffect } from 'react';
|
||||
* @returns isLoading boolean to indicate if the callback is currently being executed
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function useInfiniteScroll(
|
||||
callback: () => Promise<void> | void,
|
||||
deps?: React.DependencyList | undefined
|
||||
@@ -13,13 +16,15 @@ export default function useInfiniteScroll(
|
||||
const isScrolling = () => {
|
||||
const { innerHeight } = window;
|
||||
const { scrollTop, offsetHeight } = document.documentElement;
|
||||
if (innerHeight + scrollTop >= offsetHeight - 100) {
|
||||
if (innerHeight + scrollTop >= offsetHeight - 650) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', isScrolling);
|
||||
window.addEventListener('scroll', isScrolling, {
|
||||
passive: true,
|
||||
});
|
||||
return () => window.removeEventListener('scroll', isScrolling);
|
||||
}, deps);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Serialized } from 'chrome-extension-toolkit';
|
||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { UserScheduleStore } from 'src/shared/storage/UserScheduleStore';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
|
||||
export default function useSchedules(): [active: UserSchedule | null, schedules: UserSchedule[]] {
|
||||
const [schedules, setSchedules] = useState<UserSchedule[]>([]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import TAB_MESSAGES from '@shared/messages/TabMessages';
|
||||
import { createUseMessage } from 'chrome-extension-toolkit';
|
||||
import TAB_MESSAGES from 'src/shared/messages/TabMessages';
|
||||
|
||||
export const useTabMessage = createUseMessage<TAB_MESSAGES>();
|
||||
export const useTabMessage = createUseMessage<TAB_MESSAGES>();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Serialized } from 'chrome-extension-toolkit';
|
||||
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ExtensionStore } from 'src/shared/storage/ExtensionStore';
|
||||
|
||||
export default function useVersion(): string {
|
||||
const [version, setVersion] = useState<string>('');
|
||||
@@ -17,4 +16,3 @@ export default function useVersion(): string {
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ContextInvalidated, createShadowDOM, onContextInvalidated } from 'chrome-extension-toolkit';
|
||||
import React from 'react';
|
||||
import { background } from 'src/shared/messages';
|
||||
import render from './lib/react';
|
||||
|
||||
import { ContextInvalidated, createShadowDOM, isExtensionPopup, onContextInvalidated } from 'chrome-extension-toolkit';
|
||||
import CourseCatalogMain from './components/CourseCatalogMain';
|
||||
import colors from './styles/colors.module.scss';
|
||||
import getSiteSupport, { SiteSupport } from './lib/getSiteSupport';
|
||||
import PopupMain from './components/PopupMain';
|
||||
import getSiteSupport, { SiteSupport } from './lib/getSiteSupport';
|
||||
import colors from './styles/colors.module.scss';
|
||||
|
||||
const support = getSiteSupport(window.location.href);
|
||||
console.log('support:', support);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Serialized } from 'chrome-extension-toolkit';
|
||||
import { Course, Status, InstructionMode, ScrapedRow, Semester } from 'src/shared/types/Course';
|
||||
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
|
||||
import Instructor from 'src/shared/types/Instructor';
|
||||
import { SiteSupport } from 'src/views/lib/getSiteSupport';
|
||||
import { Course, InstructionMode, ScrapedRow, Semester, Status } from '@shared/types/Course';
|
||||
import { CourseSchedule } from '@shared/types/CourseSchedule';
|
||||
import Instructor from '@shared/types/Instructor';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
|
||||
/**
|
||||
* The selectors that we use to scrape the course catalog list table (https://utdirect.utexas.edu/apps/registrar/course_schedule/20239/results/?fos_fl=C+S&level=U&search_type_main=FIELD)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import initSqlJs from 'sql.js/dist/sql-wasm';
|
||||
|
||||
const WASM_FILE_URL = chrome.runtime.getURL('database/sql-wasm.wasm');
|
||||
const DB_FILE_URL = chrome.runtime.getURL('database/grades.db');
|
||||
import DB_FILE_URL from '@public/database/grades.db?url';
|
||||
import WASM_FILE_URL from 'sql.js/dist/sql-wasm.wasm?url';
|
||||
// import WASM_FILE_URL from '../../../../public/database/sql-wasm.wasm?url';
|
||||
|
||||
/**
|
||||
* A utility type for the SQL.js Database type
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Course, Semester } from 'src/shared/types/Course';
|
||||
import { CourseSQLRow, Distribution } from 'src/shared/types/Distribution';
|
||||
import { Course, Semester } from '@shared/types/Course';
|
||||
import { CourseSQLRow, Distribution } from '@shared/types/Distribution';
|
||||
import { initializeDB } from './initializeDB';
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ export enum AutoLoadStatus {
|
||||
LOADING = 'LOADING',
|
||||
IDLE = 'IDLE',
|
||||
ERROR = 'ERROR',
|
||||
DONE = 'DONE',
|
||||
}
|
||||
|
||||
let isLoading = false;
|
||||
@@ -24,7 +25,7 @@ let nextPageURL = getNextButton(document)?.href;
|
||||
export async function loadNextCourseCatalogPage(): Promise<[AutoLoadStatus, HTMLTableRowElement[]]> {
|
||||
// if there is no more nextPageURL, then we have reached the end of the course catalog, so we can stop
|
||||
if (!nextPageURL) {
|
||||
return [AutoLoadStatus.IDLE, []];
|
||||
return [AutoLoadStatus.DONE, []];
|
||||
}
|
||||
// remove the next button so that we don't load the same page twice
|
||||
removePaginationButtons(document);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import './colors.module.scss';
|
||||
@import './fonts.module.scss';
|
||||
@import './elevation.module.scss';
|
||||
@import './utils.module.scss';
|
||||
@use 'colors.module.scss';
|
||||
@use 'fonts.module.scss';
|
||||
@use 'elevation.module.scss';
|
||||
@use 'utils.module.scss';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@each $weights in '100' '200' '300' '400' '500' '600' '700' '800' '900' {
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: url('chrome-extension://__MSG_@@extension_id__/fonts/inter-#{$weights}.woff2') format('woff2');
|
||||
src: url('@public/fonts/inter-#{$weights}.woff2') format('woff2');
|
||||
font-display: auto;
|
||||
font-style: normal;
|
||||
font-weight: #{$weights};
|
||||
@@ -13,7 +13,7 @@
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url('chrome-extension://__MSG_@@extension_id__/fonts/material-icons.woff2') format('woff2');
|
||||
src: url('@public/fonts/material-icons.woff2') format('woff2');
|
||||
}
|
||||
|
||||
$light_weight: 300;
|
||||
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user