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: open an injected course page on course block click in popup main #146

Merged
merged 29 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a8371d5
feat: Imports to popupcourseblock.tsx
DereC4 Mar 11, 2024
ce23345
changing the blocks to accept parameters for clicking functionality w…
DereC4 Mar 11, 2024
c11a3ec
put the click parameter in the div of popupcourseblock
DereC4 Mar 11, 2024
c832953
safely calling for onCourseClick in the event it is an undefined func…
DereC4 Mar 11, 2024
1b52acd
handled other calls of popupcourseblock with empty functions for now,…
DereC4 Mar 11, 2024
1845731
feat: Testing out passing params to handleOpenCalendar
DereC4 Mar 11, 2024
51024d9
url that takes in params to open calendar with params
DereC4 Mar 11, 2024
445b0de
further work on url params; from popup main to handleopencalendar to …
DereC4 Mar 11, 2024
5a85633
Merge branch 'master' into derek/popupInject
DereC4 Mar 15, 2024
2683383
feat: small calendar shifting after merge:
DereC4 Mar 15, 2024
f8bd5b3
fix: merge handling and then references to new click parameter
DereC4 Mar 15, 2024
1b97d39
fix: optional params
DereC4 Mar 15, 2024
1a984fe
feat: split into two functions instead
DereC4 Mar 15, 2024
fd6a47e
fix: changing proper usage of handleOpenCalendarWithCourse
DereC4 Mar 15, 2024
735996a
feat: show course popup when calendar opened
Samathingamajig Mar 15, 2024
f66e494
chore: remove useless commented out code
Samathingamajig Mar 15, 2024
27c611a
feat: close popup on calendar nav, fix build errors, remove useless c…
Samathingamajig Mar 15, 2024
51af0de
chore: chromatic so dumb fr why aren't you chrome
Samathingamajig Mar 15, 2024
771b191
fix: refactor listeners to build properly
Samathingamajig Mar 15, 2024
8d28ad5
feat: exit early when not in chrome extension
Samathingamajig Mar 15, 2024
b4da883
fix: function return type
DereC4 Mar 16, 2024
e8f9ddf
fix: function return type x2
DereC4 Mar 16, 2024
0f12ef7
fix: generic type for useState
DereC4 Mar 16, 2024
43d85d5
refactor: extract calendar opening on click functions
Samathingamajig Mar 16, 2024
2842b4c
refactor: chrome runtime mock, omit question mark if no query params,…
Samathingamajig Mar 16, 2024
83bffd2
refactor: move course click event into component directly instead of …
Samathingamajig Mar 16, 2024
4e278b6
refactor: removed useless wrapper functions, made popup course block …
Samathingamajig Mar 16, 2024
ffc9b2b
Merge branch 'main' into derek/popupInject
Samathingamajig Mar 16, 2024
ba91c09
fix: i dont wanna talk about it
Samathingamajig Mar 16, 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
23 changes: 23 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ globalThis.chrome = {
},
},
},
runtime: {
id: 'fake-id',
getManifest(): chrome.runtime.Manifest {
return {
manifest_version: 3,
name: 'fake-name',
version: '0.0.0',
};
},
onMessage: {
/**
* Registers an event listener callback to an event.
* @param callback Called when an event occurs. The parameters of this function depend on the type of event.
*/
addListener<T extends Function>(callback: T) {},

/**
* Deregisters an event listener callback from an event.
* @param callback Listener that shall be unregistered.
*/
removeListener<T extends Function>(callback: T) {},
},
},
Razboy20 marked this conversation as resolved.
Show resolved Hide resolved
} as typeof chrome;

export default preview;
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@hello-pangea/dnd": "^16.5.0",
"@unocss/vite": "^0.58.6",
"@vitejs/plugin-react": "^4.2.1",
"chrome-extension-toolkit": "^0.0.51",
"chrome-extension-toolkit": "^0.0.54",
"clsx": "^2.1.0",
"highcharts": "^11.3.0",
"highcharts-react-official": "^3.2.1",
Expand Down Expand Up @@ -120,4 +120,4 @@
"es-module-lexer": "^1.4.1"
}
}
}
}
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/pages/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import onInstall from './events/onInstall';
import onServiceWorkerAlive from './events/onServiceWorkerAlive';
import onUpdate from './events/onUpdate';
import browserActionHandler from './handler/browserActionHandler';
import calendarBackgroundHandler from './handler/calendarBackgroundHandler';
import CESHandler from './handler/CESHandler';
import tabManagementHandler from './handler/tabManagementHandler';
import userScheduleHandler from './handler/userScheduleHandler';
Expand Down Expand Up @@ -36,6 +37,7 @@ const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
...tabManagementHandler,
...userScheduleHandler,
...CESHandler,
...calendarBackgroundHandler,
});

messageListener.listen();
Expand Down
44 changes: 44 additions & 0 deletions src/pages/background/handler/calendarBackgroundHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import openNewTab from '@background/util/openNewTab';
import { tabs } from '@shared/messages';
import type { CalendarBackgroundMessages } from '@shared/messages/CalendarMessages';
import type { MessageHandler } from 'chrome-extension-toolkit';

const getAllTabInfos = async () => {
const openTabs = await chrome.tabs.query({});
const results = await Promise.allSettled(openTabs.map(tab => tabs.getTabInfo(undefined, tab.id)));
return results
.map((result, index) => ({ result, index }))
.filter(({ result }) => result.status === 'fulfilled')
.map(({ result, index }) => {
if (result.status !== 'fulfilled') throw new Error('Will never happen, typescript dumb');
return {
...result.value,
tab: openTabs[index],
};
});
};

const calendarBackgroundHandler: MessageHandler<CalendarBackgroundMessages> = {
async switchToCalendarTab({ data, sendResponse }) {
const { uniqueId } = data;
const calendarUrl = chrome.runtime.getURL(`calendar.html`);

const allTabs = await getAllTabInfos();

const openCalendarTabInfo = allTabs.find(tab => tab.url.startsWith(calendarUrl));

if (openCalendarTabInfo !== undefined) {
chrome.tabs.update(openCalendarTabInfo.tab.id, { active: true });
if (uniqueId !== undefined) await tabs.openCoursePopup({ uniqueId }, openCalendarTabInfo.tab.id);
sendResponse(openCalendarTabInfo.tab);
} else {
const urlParams = new URLSearchParams();
if (uniqueId !== undefined) urlParams.set('uniqueId', uniqueId.toString());
const url = `${calendarUrl}?${urlParams.toString()}`.replace(/\?$/, '');
const tab = await openNewTab(url);
sendResponse(tab);
}
},
};

export default calendarBackgroundHandler;
19 changes: 19 additions & 0 deletions src/shared/messages/CalendarMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interface CalendarBackgroundMessages {
/**
* Opens the calendar page if it is not already open, focuses the tab, and optionally opens the calendar for a specific course
*
* @param data - The unique id of the course to open the calendar page for (optional)
*/
switchToCalendarTab: (data: { uniqueId?: number }) => chrome.tabs.Tab;
}

interface CalendarTabMessages {
/**
* Opens a popup for a specific course on the calendar page
*
* @param data - The unique id of the course to open on the calendar page
*/
openCoursePopup: (data: { uniqueId: number }) => chrome.tabs.Tab;
}

export type { CalendarBackgroundMessages, CalendarTabMessages };
15 changes: 15 additions & 0 deletions src/shared/messages/TabInfoMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
type TabInfo = {
url: string;
title: string;
};

interface TabInfoMessages {
/**
* Gets the info for the tab receiving the message
*
* @returns The info for the tab receiving the message
*/
getTabInfo: () => TabInfo;
}

export default TabInfoMessages;
doprz marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 0 additions & 4 deletions src/shared/messages/TabMessages.ts

This file was deleted.

16 changes: 13 additions & 3 deletions src/shared/messages/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { createMessenger } from 'chrome-extension-toolkit';

import type BrowserActionMessages from './BrowserActionMessages';
import type { CalendarBackgroundMessages, CalendarTabMessages } from './CalendarMessages';
import type CESMessage from './CESMessage';
import type TabInfoMessages from './TabInfoMessages';
import type TabManagementMessages from './TabManagementMessages';
import type TAB_MESSAGES from './TabMessages';
import type { 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 & UserScheduleMessages & CESMessage;
export type BACKGROUND_MESSAGES = BrowserActionMessages &
TabManagementMessages &
UserScheduleMessages &
CESMessage &
CalendarBackgroundMessages;

/**
* This is a type with all the message definitions that can be sent TO specific tabs
*/
export type TAB_MESSAGES = CalendarTabMessages & TabInfoMessages;

/**
* A utility object that can be used to send type-safe messages to the background script
Expand All @@ -19,4 +29,4 @@ export const background = createMessenger<BACKGROUND_MESSAGES>('background');
/**
* A utility object that can be used to send type-safe messages to specific tabs
*/
export const tabs = createMessenger<TAB_MESSAGES>('tab');
export const tabs = createMessenger<TAB_MESSAGES>('foreground');
4 changes: 2 additions & 2 deletions src/stories/components/PopupCourseBlock.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ export const Variants: Story = {
};

export const AllColors: Story = {
render: props => (
render: () => (
<div className='grid grid-flow-col grid-cols-2 grid-rows-9 max-w-2xl w-90vw gap-x-4 gap-y-2'>
{tailwindColorways.map((color, i) => (
{tailwindColorways.map(color => (
<PopupCourseBlock key={color.primaryColor} course={ExampleCourse} colors={color} />
))}
</div>
Expand Down
10 changes: 8 additions & 2 deletions src/views/components/PopupMain.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { background } from '@shared/messages';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { tailwindColorways } from '@shared/util/storybook';
import Divider from '@views/components/common/Divider/Divider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import List from '@views/components/common/List/List';
import Text from '@views/components/common/Text/Text';
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
Expand Down Expand Up @@ -34,6 +34,11 @@ export default function PopupMain(): JSX.Element {
await openTabFromContentScript(url);
};

const handleCalendarOpenOnClick = async () => {
await background.switchToCalendarTab({});
window.close();
};

return (
<ExtensionRoot>
<div className='h-screen max-h-full flex flex-col bg-white'>
Expand All @@ -50,7 +55,7 @@ export default function PopupMain(): JSX.Element {
</div>
</div>
<div className='flex items-center gap-2.5'>
<button className='bg-ut-burntorange px-2 py-1.25 btn' onClick={handleOpenCalendar}>
<button className='bg-ut-burntorange px-2 py-1.25 btn' onClick={handleCalendarOpenOnClick}>
<CalendarIcon className='size-6 text-white' />
</button>
<button className='bg-transparent px-2 py-1.25 btn' onClick={handleOpenOptions}>
Expand Down Expand Up @@ -118,6 +123,7 @@ export default function PopupMain(): JSX.Element {
<CourseStatus status='WAITLISTED' size='mini' />
<CourseStatus status='CLOSED' size='mini' />
<CourseStatus status='CANCELLED' size='mini' />
``
</div>
<div className='inline-flex items-center self-center gap-1'>
<Text variant='mini' className='text-ut-gray'>
Expand Down
34 changes: 32 additions & 2 deletions src/views/components/calendar/Calendar/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CalendarTabMessages } from '@shared/messages/CalendarMessages';
import type { Course } from '@shared/types/Course';
import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar/CalendarBottomBar';
import CalendarGrid from '@views/components/calendar/CalendarGrid/CalendarGrid';
Expand All @@ -7,6 +8,7 @@ import ImportantLinks from '@views/components/calendar/ImportantLinks';
import Divider from '@views/components/common/Divider/Divider';
import CourseCatalogInjectedPopup from '@views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import { useFlattenedCourseSchedule } from '@views/hooks/useFlattenedCourseSchedule';
import { MessageListener } from 'chrome-extension-toolkit';
import React, { useEffect, useRef, useState } from 'react';

import styles from './Calendar.module.scss';
Expand All @@ -17,11 +19,39 @@ import styles from './Calendar.module.scss';
export default function Calendar(): JSX.Element {
const calendarRef = useRef<HTMLDivElement>(null);
const { courseCells, activeSchedule } = useFlattenedCourseSchedule();
const [course, setCourse] = useState<Course | null>(null);
const [showPopup, setShowPopup] = useState(false);
const [course, setCourse] = useState<Course | null>((): Course | null => {
const urlParams = new URLSearchParams(window.location.search);
const uniqueIdRaw = urlParams.get('uniqueId');
if (uniqueIdRaw === null) return null;
const uniqueId = Number(uniqueIdRaw);
const course = activeSchedule.courses.find(course => course.uniqueId === uniqueId);
if (course === undefined) return null;
urlParams.delete('uniqueId');
const newUrl = `${window.location.pathname}?${urlParams}`.replace(/\?$/, '');
window.history.replaceState({}, '', newUrl);
return course;
});

const [showPopup, setShowPopup] = useState<boolean>(course !== null);
const [sidebarWidth, setSidebarWidth] = useState('20%');
const [scale, setScale] = useState(1);

useEffect(() => {
const listener = new MessageListener<CalendarTabMessages>({
async openCoursePopup({ data, sendResponse }) {
const course = activeSchedule.courses.find(course => course.uniqueId === data.uniqueId);
if (course === undefined) return;
setCourse(course);
setShowPopup(true);
sendResponse(await chrome.tabs.getCurrent());
},
});

listener.listen();

return () => listener.unlisten();
DereC4 marked this conversation as resolved.
Show resolved Hide resolved
}, [activeSchedule.courses]);

useEffect(() => {
const adjustLayout = () => {
const windowHeight = window.innerHeight;
Expand Down
20 changes: 19 additions & 1 deletion src/views/components/common/ExtensionRoot/ExtensionRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
// import '@unocss/reset/tailwind-compat.css';
import 'uno.css';

import React from 'react';
import type TabInfoMessages from '@shared/messages/TabInfoMessages';
import { MessageListener } from 'chrome-extension-toolkit';
import React, { useEffect } from 'react';

import styles from './ExtensionRoot.module.scss';

interface Props {
testId?: string;
}

/**
* A wrapper component for the extension elements that adds some basic styling to them
*/
export default function ExtensionRoot(props: React.PropsWithChildren<Props>): JSX.Element {
useEffect(() => {
const tabInfoListener = new MessageListener<TabInfoMessages>({
getTabInfo: ({ sendResponse }) => {
sendResponse({
url: window.location.href,
title: document.title,
});
},
});

tabInfoListener.listen();

return () => tabInfoListener.unlisten();
}, []);

return (
<div className={styles.extensionRoot} data-testid={props.testId}>
{props.children}
Expand Down
16 changes: 13 additions & 3 deletions src/views/components/common/PopupCourseBlock/PopupCourseBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { background } from '@shared/messages';
import type { Course } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type { CourseColors } from '@shared/util/colors';
Expand Down Expand Up @@ -34,12 +35,21 @@ export default function PopupCourseBlock({
const fontColor = pickFontColor(colors.primaryColor);
const formattedUniqueId = course.uniqueId.toString().padStart(5, '0');

const handleClick = async () => {
await background.switchToCalendarTab({ uniqueId: course.uniqueId });
window.close();
};

return (
<div
<button
style={{
backgroundColor: colors.primaryColor,
}}
className={clsx('h-full w-full inline-flex items-center justify-center gap-1 rounded pr-3', className)}
className={clsx(
'h-full w-full inline-flex items-center justify-center gap-1 rounded pr-3 cursor-pointer focusable text-left',
className
)}
onClick={handleClick}
>
<div
style={{
Expand All @@ -64,6 +74,6 @@ export default function PopupCourseBlock({
<StatusIcon status={course.status} className='h-5 w-5' />
</div>
)}
</div>
</button>
);
}
Loading
Loading