feat: open an injected course page on course block click in popup main (#146)
* feat: Imports to popupcourseblock.tsx * changing the blocks to accept parameters for clicking functionality which may or may not open the calendar * put the click parameter in the div of popupcourseblock * safely calling for onCourseClick in the event it is an undefined function * handled other calls of popupcourseblock with empty functions for now, and i think popupmain opens calendar now when the course block is clicked * feat: Testing out passing params to handleOpenCalendar * url that takes in params to open calendar with params * further work on url params; from popup main to handleopencalendar to calendar using urlsearchparams * feat: small calendar shifting after merge: * fix: merge handling and then references to new click parameter * fix: optional params * feat: split into two functions instead * fix: changing proper usage of handleOpenCalendarWithCourse * feat: show course popup when calendar opened * chore: remove useless commented out code * feat: close popup on calendar nav, fix build errors, remove useless comments/logs * chore: chromatic so dumb fr why aren't you chrome * fix: refactor listeners to build properly * feat: exit early when not in chrome extension * fix: function return type * fix: function return type x2 * fix: generic type for useState * refactor: extract calendar opening on click functions * refactor: chrome runtime mock, omit question mark if no query params, rename calendar event * refactor: move course click event into component directly instead of prop * refactor: removed useless wrapper functions, made popup course block more accessible * fix: i dont wanna talk about it --------- Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
This commit is contained in:
@@ -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';
|
||||
@@ -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'>
|
||||
@@ -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}>
|
||||
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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();
|
||||
}, [activeSchedule.courses]);
|
||||
|
||||
useEffect(() => {
|
||||
const adjustLayout = () => {
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
@@ -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={{
|
||||
@@ -64,6 +74,6 @@ export default function PopupCourseBlock({
|
||||
<StatusIcon status={course.status} className='h-5 w-5' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,15 +29,6 @@ interface HeadingAndActionProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the calendar in a new tab.
|
||||
* @returns {Promise<void>} A promise that resolves when the tab is opened.
|
||||
*/
|
||||
export const handleOpenCalendar = async (): Promise<void> => {
|
||||
const url = chrome.runtime.getURL('calendar.html');
|
||||
openNewTab({ url });
|
||||
};
|
||||
|
||||
/**
|
||||
* Capitalizes the first letter of a string and converts the rest of the letters to lowercase.
|
||||
*
|
||||
@@ -174,7 +165,12 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
|
||||
</div>
|
||||
</div>
|
||||
<div className='my-3 flex flex-wrap items-center gap-x-3.75 gap-y-2.5'>
|
||||
<Button variant='filled' color='ut-burntorange' icon={CalendarMonth} onClick={handleOpenCalendar} />
|
||||
<Button
|
||||
variant='filled'
|
||||
color='ut-burntorange'
|
||||
icon={CalendarMonth}
|
||||
onClick={() => background.switchToCalendarTab({})}
|
||||
/>
|
||||
<Divider size='1.75rem' orientation='vertical' />
|
||||
<Button variant='outline' color='ut-blue' icon={Reviews} onClick={handleOpenRateMyProf}>
|
||||
RateMyProf
|
||||
|
||||
Reference in New Issue
Block a user