refactoring, using different pattern for page injection and reusing same pattern for both popup and content scripts

This commit is contained in:
Sriram Hariharan
2023-03-03 21:13:43 -06:00
parent 4ed52a3c9f
commit beb61176c1
18 changed files with 120 additions and 95 deletions

View File

@@ -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 <Button onClick={openGoogle}>{props.support.join(',')}</Button>;
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import styles from './Button.module.scss';
interface Props {
onClick?: () => void;
}
export function Button(props: React.PropsWithChildren<Props>): JSX.Element {
return (
<button className={styles.button} onClick={props.onClick}>
{props.children}
</button>
);
}

View File

@@ -1,5 +0,0 @@
import React from 'react';
export default function ContentMain() {
return <div>content</div>;
}

View File

@@ -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 (
<button className={styles.button} onClick={handleOpenUrl('https://www.google.com')}>
Click me
</button>
);
}

View File

@@ -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(<ContentMain />, shadowDom.shadowRoot);
shadowDom.addStyle('static/css/content.css');
}
onContextInvalidated(() => {
const div = document.createElement('div');
div.id = 'context-invalidated-container';
document.body.appendChild(div);
render(
<ContextInvalidated fontFamily='monospace' color={colors.WHITE} backgroundColor={colors.BURNT_ORANGE} />,
div
);
});

View File

@@ -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 [];
}

37
src/views/index.tsx Normal file
View File

@@ -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(<PopupMain />, document.getElementById('root'));
}
if (support.includes(SiteSupport.COURSE_CATALOG)) {
const shadowDom = createShadowDOM('ut-registration-plus-dom-container');
render(<CourseCatalogMain support={support} />, 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(
<ContextInvalidated fontFamily='monospace' color={colors.WHITE} backgroundColor={colors.BURNT_ORANGE} />,
div
);
});

View File

@@ -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 [];
}

View File

@@ -1,5 +0,0 @@
import React from 'react';
import { render } from 'react-dom';
import PopupMain from './PopupMain';
render(<PopupMain />, document.getElementById('root'));

View File

@@ -1,24 +0,0 @@
// this is a custom wrapper around react-devtools
// that changes it so that we only send messages to the devtools when the current tab is active;
import { connectToDevTools } from 'react-devtools-core';
// connect to the devtools server
let ws = new WebSocket('ws://localhost:8097');
connectToDevTools({
websocket: ws,
});
// when the tab's visibile state changes, we connect or disconnect from the devtools
const onVisibilityChange = () => {
if (document.visibilityState === 'visible') {
ws = new WebSocket('ws://localhost:8097');
connectToDevTools({
websocket: ws,
});
} else {
ws.close();
}
};
document.addEventListener('visibilitychange', onVisibilityChange);