From beb61176c17912f66922238f64a63f69e9bc7bb0 Mon Sep 17 00:00:00 2001 From: Sriram Hariharan Date: Fri, 3 Mar 2023 21:13:43 -0600 Subject: [PATCH] refactoring, using different pattern for page injection and reusing same pattern for both popup and content scripts --- package-lock.json | 11 ++++++ package.json | 3 +- src/{views => debug}/reactDevtools.ts | 0 src/views/components/CourseCatalogMain.tsx | 16 ++++++++ src/views/{popup => components}/PopupMain.tsx | 0 .../common}/Button/Button.module.scss | 0 src/views/components/common/Button/Button.tsx | 14 +++++++ src/views/content/ContentMain.tsx | 5 --- .../content/components/Button/Button.tsx | 15 -------- src/views/content/content.module.scss | 0 src/views/content/content.tsx | 29 --------------- src/views/content/lib/getPageTypes.ts | 36 ------------------ src/views/index.tsx | 37 +++++++++++++++++++ .../{content => }/lib/courseSchedule/index.ts | 0 .../courseSchedule/populateSearchInputs.ts | 0 src/views/lib/getSiteSupport.ts | 36 ++++++++++++++++++ src/views/popup/popup.tsx | 5 --- webpack/webpack.config.ts | 8 ++-- 18 files changed, 120 insertions(+), 95 deletions(-) rename src/{views => debug}/reactDevtools.ts (100%) create mode 100644 src/views/components/CourseCatalogMain.tsx rename src/views/{popup => components}/PopupMain.tsx (100%) rename src/views/{content/components => components/common}/Button/Button.module.scss (100%) create mode 100644 src/views/components/common/Button/Button.tsx delete mode 100644 src/views/content/ContentMain.tsx delete mode 100644 src/views/content/components/Button/Button.tsx delete mode 100644 src/views/content/content.module.scss delete mode 100644 src/views/content/content.tsx delete mode 100644 src/views/content/lib/getPageTypes.ts create mode 100644 src/views/index.tsx rename src/views/{content => }/lib/courseSchedule/index.ts (100%) rename src/views/{content => }/lib/courseSchedule/populateSearchInputs.ts (100%) create mode 100644 src/views/lib/getSiteSupport.ts delete mode 100644 src/views/popup/popup.tsx diff --git a/package-lock.json b/package-lock.json index 4b2fabbd..c1547496 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "chrome-extension-toolkit": "^0.0.22", + "classnames": "^2.3.2", "clean-webpack-plugin": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -4702,6 +4703,11 @@ "node": ">=6.0" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -20674,6 +20680,11 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", diff --git a/package.json b/package.json index 1435b782..3a05fe5d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "chrome-extension-toolkit": "^0.0.22", + "classnames": "^2.3.2", "clean-webpack-plugin": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -75,4 +76,4 @@ "webpack-build-notifier": "^2.3.0", "webpack-dev-server": "^4.11.1" } -} \ No newline at end of file +} diff --git a/src/views/reactDevtools.ts b/src/debug/reactDevtools.ts similarity index 100% rename from src/views/reactDevtools.ts rename to src/debug/reactDevtools.ts diff --git a/src/views/components/CourseCatalogMain.tsx b/src/views/components/CourseCatalogMain.tsx new file mode 100644 index 00000000..5755c6b2 --- /dev/null +++ b/src/views/components/CourseCatalogMain.tsx @@ -0,0 +1,16 @@ +import React, { useEffect, useMemo } from 'react'; +import { bMessenger } from 'src/shared/messages'; +import { SiteSupport } from '../lib/getSiteSupport'; +import { Button } from './common/Button/Button'; + +interface Props { + support: SiteSupport[]; +} + +export default function CourseCatalogMain(props: Props) { + const openGoogle = () => { + bMessenger.openNewTab({ url: 'https://google.com' }); + }; + + return ; +} diff --git a/src/views/popup/PopupMain.tsx b/src/views/components/PopupMain.tsx similarity index 100% rename from src/views/popup/PopupMain.tsx rename to src/views/components/PopupMain.tsx diff --git a/src/views/content/components/Button/Button.module.scss b/src/views/components/common/Button/Button.module.scss similarity index 100% rename from src/views/content/components/Button/Button.module.scss rename to src/views/components/common/Button/Button.module.scss diff --git a/src/views/components/common/Button/Button.tsx b/src/views/components/common/Button/Button.tsx new file mode 100644 index 00000000..b3aa96d4 --- /dev/null +++ b/src/views/components/common/Button/Button.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import styles from './Button.module.scss'; + +interface Props { + onClick?: () => void; +} + +export function Button(props: React.PropsWithChildren): JSX.Element { + return ( + + ); +} diff --git a/src/views/content/ContentMain.tsx b/src/views/content/ContentMain.tsx deleted file mode 100644 index 411a04fe..00000000 --- a/src/views/content/ContentMain.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export default function ContentMain() { - return
content
; -} diff --git a/src/views/content/components/Button/Button.tsx b/src/views/content/components/Button/Button.tsx deleted file mode 100644 index d75c42e9..00000000 --- a/src/views/content/components/Button/Button.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { bMessenger } from 'src/shared/messages'; -import styles from './Button.module.scss'; - -export function Button(): JSX.Element { - const handleOpenUrl = (url: string) => () => { - bMessenger.openNewTab({ url }); - }; - - return ( - - ); -} diff --git a/src/views/content/content.module.scss b/src/views/content/content.module.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/views/content/content.tsx b/src/views/content/content.tsx deleted file mode 100644 index ddf51144..00000000 --- a/src/views/content/content.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { render } from 'react-dom'; -import { ContextInvalidated, createShadowDOM, onContextInvalidated } from 'chrome-extension-toolkit'; -import ContentMain from './ContentMain'; -import colors from '../styles/colors.module.scss'; -import getPageTypes, { PageType } from './lib/getPageTypes'; -import { populateSearchInputs } from './lib/courseSchedule/populateSearchInputs'; - -const pageTypes = getPageTypes(window.location.href); -console.log('pageTypes:', pageTypes); - -if (pageTypes.includes(PageType.COURSE_SCHEDULE)) { - if (pageTypes.includes(PageType.COURSE_SCHEDULE_LIST)) { - populateSearchInputs(); - } - const shadowDom = createShadowDOM('ut-registration-plus-dom-container'); - render(, shadowDom.shadowRoot); - shadowDom.addStyle('static/css/content.css'); -} - -onContextInvalidated(() => { - const div = document.createElement('div'); - div.id = 'context-invalidated-container'; - document.body.appendChild(div); - render( - , - div - ); -}); diff --git a/src/views/content/lib/getPageTypes.ts b/src/views/content/lib/getPageTypes.ts deleted file mode 100644 index f5294aaa..00000000 --- a/src/views/content/lib/getPageTypes.ts +++ /dev/null @@ -1,36 +0,0 @@ -export enum PageType { - EXTENSION_POPUP, - COURSE_SCHEDULE, - COURSE_SCHEDULE_LIST, - COURSE_DETAILS, - UT_PLANNER, - WAITLIST, -} - -/** - * We use this function to determine what page the user is on, and then we can use that information to determine what to do - * @param url the url of the current page - * @returns a list of page types that the current page is - */ -export default function getPageTypes(url: string): PageType[] { - if (url.includes('chrome-extension://')) { - return [PageType.EXTENSION_POPUP]; - } - if (url.includes('utexas.collegescheduler.com')) { - return [PageType.UT_PLANNER]; - } - if (url.includes('utdirect.utexas.edu/apps/registrar/course_schedule')) { - const types = [PageType.COURSE_SCHEDULE]; - if (url.includes('results')) { - types.push(PageType.COURSE_SCHEDULE_LIST); - } - if (document.querySelector('#details')) { - types.push(PageType.COURSE_DETAILS); - } - return types; - } - if (url.includes('utdirect.utexas.edu') && (url.includes('waitlist') || url.includes('classlist'))) { - return [PageType.WAITLIST]; - } - return []; -} diff --git a/src/views/index.tsx b/src/views/index.tsx new file mode 100644 index 00000000..ee88f117 --- /dev/null +++ b/src/views/index.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { render } from 'react-dom'; +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'; + +const support = getSiteSupport(window.location.href); + +if (isExtensionPopup()) { + render(, document.getElementById('root')); +} + +if (support.includes(SiteSupport.COURSE_CATALOG)) { + const shadowDom = createShadowDOM('ut-registration-plus-dom-container'); + render(, shadowDom.shadowRoot); + shadowDom.addStyle('static/css/content.css'); +} + +if (support.includes(SiteSupport.WAITLIST)) { + // TODO: Implement waitlist support +} + +if (support.includes(SiteSupport.UT_PLANNER)) { + // TODO: Implement ut planner support +} + +onContextInvalidated(() => { + const div = document.createElement('div'); + div.id = 'context-invalidated-container'; + document.body.appendChild(div); + render( + , + div + ); +}); diff --git a/src/views/content/lib/courseSchedule/index.ts b/src/views/lib/courseSchedule/index.ts similarity index 100% rename from src/views/content/lib/courseSchedule/index.ts rename to src/views/lib/courseSchedule/index.ts diff --git a/src/views/content/lib/courseSchedule/populateSearchInputs.ts b/src/views/lib/courseSchedule/populateSearchInputs.ts similarity index 100% rename from src/views/content/lib/courseSchedule/populateSearchInputs.ts rename to src/views/lib/courseSchedule/populateSearchInputs.ts diff --git a/src/views/lib/getSiteSupport.ts b/src/views/lib/getSiteSupport.ts new file mode 100644 index 00000000..bafe2ad2 --- /dev/null +++ b/src/views/lib/getSiteSupport.ts @@ -0,0 +1,36 @@ +/** + * An enum that represents the different types of pages that we support + * a given url/page can correspond to many of these enum values + */ +export enum SiteSupport { + COURSE_CATALOG = 'COURSE_CATALOG', + COURSE_CATALOG_LIST = 'COURSE_CATALOG_LIST', + COURSE_CATALOG_DETAILS = 'COURSE_CATALOG_DETAILS', + UT_PLANNER = 'UT_PLANNER', + WAITLIST = 'WAITLIST', +} + +/** + * We use this function to determine what page the user is on, and then we can use that information to determine what to do + * @param url the url of the current page + * @returns a list of page types that the current page is + */ +export default function getSiteSupport(url: string): SiteSupport[] { + if (url.includes('utexas.collegescheduler.com')) { + return [SiteSupport.UT_PLANNER]; + } + if (url.includes('utdirect.utexas.edu/apps/registrar/course_schedule')) { + const types = [SiteSupport.COURSE_CATALOG]; + if (url.includes('results')) { + types.push(SiteSupport.COURSE_CATALOG_LIST); + } + if (document.querySelector('#details')) { + types.push(SiteSupport.COURSE_CATALOG_DETAILS); + } + return types; + } + if (url.includes('utdirect.utexas.edu') && (url.includes('waitlist') || url.includes('classlist'))) { + return [SiteSupport.WAITLIST]; + } + return []; +} diff --git a/src/views/popup/popup.tsx b/src/views/popup/popup.tsx deleted file mode 100644 index 55a219cc..00000000 --- a/src/views/popup/popup.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; -import { render } from 'react-dom'; -import PopupMain from './PopupMain'; - -render(, document.getElementById('root')); diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index de1bd25f..e0977718 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -26,9 +26,9 @@ export default function config(mode: Environment, manifest: chrome.runtime.Manif // the entry points for the extension (the files that webpack will start bundling from) const entry: Entries = { - content: [path.resolve('src', 'views', 'content', 'content')], + content: [path.resolve('src', 'views')], + popup: [path.resolve('src', 'views')], background: [path.resolve('src', 'background', 'background')], - popup: [path.resolve('src', 'views', 'popup', 'popup')], }; // the entries that need an html file to be generated @@ -39,8 +39,8 @@ export default function config(mode: Environment, manifest: chrome.runtime.Manif entry.debug = [path.resolve('src', 'debug')]; // we need to import react-devtools before the react code in development - entry.content = [path.resolve('src', 'views', 'reactDevtools'), ...entry.content]; - entry.popup = [path.resolve('src', 'views', 'reactDevtools'), ...entry.popup]; + entry.content = [path.resolve('src', 'debug', 'reactDevtools'), ...entry.content]; + entry.popup = [path.resolve('src', 'debug', 'reactDevtools'), ...entry.popup]; } /** @see https://webpack.js.org/configuration for documentation */