feat: persist sidebar toggle state (#569)

This commit is contained in:
Samuel Gunter
2025-03-26 13:50:34 -05:00
committed by GitHub
parent d014244b28
commit 695743104c
5 changed files with 53 additions and 3 deletions

View File

@@ -36,6 +36,7 @@
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",
"@phosphor-icons/react": "^2.1.7", "@phosphor-icons/react": "^2.1.7",
"@sentry/react": "^8.55.0", "@sentry/react": "^8.55.0",
"@tanstack/react-query": "^5.69.0",
"@unocss/vite": "^0.63.6", "@unocss/vite": "^0.63.6",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"chrome-extension-toolkit": "^0.0.54", "chrome-extension-toolkit": "^0.0.54",

18
pnpm-lock.yaml generated
View File

@@ -46,6 +46,9 @@ importers:
'@sentry/react': '@sentry/react':
specifier: ^8.55.0 specifier: ^8.55.0
version: 8.55.0(react@18.3.1) version: 8.55.0(react@18.3.1)
'@tanstack/react-query':
specifier: ^5.69.0
version: 5.69.0(react@18.3.1)
'@unocss/vite': '@unocss/vite':
specifier: ^0.63.6 specifier: ^0.63.6
version: 0.63.6(patch_hash=9e2d2732a6e057a2ca90fba199730f252d8b4db8631b2c6ee0854fce7771bc95)(rollup@4.34.8)(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.5)(sass@1.85.1)(terser@5.39.0)) version: 0.63.6(patch_hash=9e2d2732a6e057a2ca90fba199730f252d8b4db8631b2c6ee0854fce7771bc95)(rollup@4.34.8)(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.5)(sass@1.85.1)(terser@5.39.0))
@@ -1981,6 +1984,14 @@ packages:
'@swc/types@0.1.18': '@swc/types@0.1.18':
resolution: {integrity: sha512-NZghLaQvF3eFdj2DUjGkpwaunbZYaRcxciHINnwA4n3FrLAI8hKFOBqs2wkcOiLQfWkIdfuG6gBkNFrkPNji5g==} resolution: {integrity: sha512-NZghLaQvF3eFdj2DUjGkpwaunbZYaRcxciHINnwA4n3FrLAI8hKFOBqs2wkcOiLQfWkIdfuG6gBkNFrkPNji5g==}
'@tanstack/query-core@5.69.0':
resolution: {integrity: sha512-Kn410jq6vs1P8Nm+ZsRj9H+U3C0kjuEkYLxbiCyn3MDEiYor1j2DGVULqAz62SLZtUZ/e9Xt6xMXiJ3NJ65WyQ==}
'@tanstack/react-query@5.69.0':
resolution: {integrity: sha512-Ift3IUNQqTcaFa1AiIQ7WCb/PPy8aexZdq9pZWLXhfLcLxH0+PZqJ2xFImxCpdDZrFRZhLJrh76geevS5xjRhA==}
peerDependencies:
react: ^18 || ^19
'@tanstack/react-virtual@3.13.2': '@tanstack/react-virtual@3.13.2':
resolution: {integrity: sha512-LceSUgABBKF6HSsHK2ZqHzQ37IKV/jlaWbHm+NyTa3/WNb/JZVcThDuTainf+PixltOOcFCYXwxbLpOX9sCx+g==} resolution: {integrity: sha512-LceSUgABBKF6HSsHK2ZqHzQ37IKV/jlaWbHm+NyTa3/WNb/JZVcThDuTainf+PixltOOcFCYXwxbLpOX9sCx+g==}
peerDependencies: peerDependencies:
@@ -8621,6 +8632,13 @@ snapshots:
dependencies: dependencies:
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
'@tanstack/query-core@5.69.0': {}
'@tanstack/react-query@5.69.0(react@18.3.1)':
dependencies:
'@tanstack/query-core': 5.69.0
react: 18.3.1
'@tanstack/react-virtual@3.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@tanstack/react-virtual@3.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@tanstack/virtual-core': 3.13.2 '@tanstack/virtual-core': 3.13.2

View File

@@ -18,6 +18,9 @@ export interface IOptionsStore {
/** whether we should open the calendar in a new tab; default is to focus an existing calendar tab */ /** whether we should open the calendar in a new tab; default is to focus an existing calendar tab */
alwaysOpenCalendarInNewTab: boolean; alwaysOpenCalendarInNewTab: boolean;
/** whether the calendar sidebar should be shown when the calendar is opened */
showCalendarSidebar: boolean;
} }
export const OptionsStore = createSyncStore<IOptionsStore>({ export const OptionsStore = createSyncStore<IOptionsStore>({
@@ -26,6 +29,7 @@ export const OptionsStore = createSyncStore<IOptionsStore>({
enableScrollToLoad: true, enableScrollToLoad: true,
enableDataRefreshing: false, enableDataRefreshing: false,
alwaysOpenCalendarInNewTab: false, alwaysOpenCalendarInNewTab: false,
showCalendarSidebar: true,
}); });
/** /**
@@ -40,6 +44,7 @@ export const initSettings = async () =>
enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'), enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'),
enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'), enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'),
alwaysOpenCalendarInNewTab: await OptionsStore.get('alwaysOpenCalendarInNewTab'), alwaysOpenCalendarInNewTab: await OptionsStore.get('alwaysOpenCalendarInNewTab'),
showCalendarSidebar: await OptionsStore.get('showCalendarSidebar'),
}) satisfies IOptionsStore; }) satisfies IOptionsStore;
// Clothing retailer right // Clothing retailer right

View File

@@ -1,8 +1,10 @@
import { Sidebar } from '@phosphor-icons/react'; import { Sidebar } from '@phosphor-icons/react';
import type { CalendarTabMessages } from '@shared/messages/CalendarMessages'; import type { CalendarTabMessages } from '@shared/messages/CalendarMessages';
import { OptionsStore } from '@shared/storage/OptionsStore';
import type { Course } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
import { CRX_PAGES } from '@shared/types/CRXPages'; import { CRX_PAGES } from '@shared/types/CRXPages';
import { openReportWindow } from '@shared/util/openReportWindow'; import { openReportWindow } from '@shared/util/openReportWindow';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar'; import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar';
import CalendarGrid from '@views/components/calendar/CalendarGrid'; import CalendarGrid from '@views/components/calendar/CalendarGrid';
import CalendarHeader from '@views/components/calendar/CalendarHeader/CalendarHeader'; import CalendarHeader from '@views/components/calendar/CalendarHeader/CalendarHeader';
@@ -16,6 +18,7 @@ import { useFlattenedCourseSchedule } from '@views/hooks/useFlattenedCourseSched
import useWhatsNewPopUp from '@views/hooks/useWhatsNew'; import useWhatsNewPopUp from '@views/hooks/useWhatsNew';
import { MessageListener } from 'chrome-extension-toolkit'; import { MessageListener } from 'chrome-extension-toolkit';
import clsx from 'clsx'; import clsx from 'clsx';
import type { ReactNode } from 'react';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import OutwardArrowIcon from '~icons/material-symbols/arrow-outward'; import OutwardArrowIcon from '~icons/material-symbols/arrow-outward';
@@ -28,15 +31,31 @@ import CalendarFooter from './CalendarFooter';
/** /**
* Calendar page component * Calendar page component
*/ */
export default function Calendar(): JSX.Element { export default function Calendar(): ReactNode {
const { courseCells, activeSchedule } = useFlattenedCourseSchedule(); const { courseCells, activeSchedule } = useFlattenedCourseSchedule();
const [course, setCourse] = useState<Course | null>(useCourseFromUrl()); const [course, setCourse] = useState<Course | null>(useCourseFromUrl());
const [showPopup, setShowPopup] = useState<boolean>(course !== null); const [showPopup, setShowPopup] = useState<boolean>(course !== null);
const [showSidebar, setShowSidebar] = useState<boolean>(true);
const showWhatsNewDialog = useWhatsNewPopUp(); const showWhatsNewDialog = useWhatsNewPopUp();
const queryClient = useQueryClient();
const { data: showSidebar, isPending: isSidebarStatePending } = useQuery({
queryKey: ['settings', 'showCalendarSidebar'],
queryFn: () => OptionsStore.get('showCalendarSidebar'),
staleTime: Infinity, // Prevent loading state on refocus
});
const { mutate: setShowSidebar } = useMutation({
mutationKey: ['settings', 'showCalendarSidebar'],
mutationFn: async (showSidebar: boolean) => {
OptionsStore.set('showCalendarSidebar', showSidebar);
},
onSuccess: (_, showSidebar) => {
queryClient.setQueryData(['settings', 'showCalendarSidebar'], showSidebar);
},
});
useEffect(() => { useEffect(() => {
const listener = new MessageListener<CalendarTabMessages>({ const listener = new MessageListener<CalendarTabMessages>({
async openCoursePopup({ data, sendResponse }) { async openCoursePopup({ data, sendResponse }) {
@@ -61,6 +80,8 @@ export default function Calendar(): JSX.Element {
if (course) setShowPopup(true); if (course) setShowPopup(true);
}, [course]); }, [course]);
if (isSidebarStatePending) return null;
return ( return (
<CalendarContext.Provider value> <CalendarContext.Provider value>
<div className='h-full w-full flex flex-col'> <div className='h-full w-full flex flex-col'>

View File

@@ -1,6 +1,7 @@
// import '@unocss/reset/tailwind-compat.css'; // import '@unocss/reset/tailwind-compat.css';
import 'uno.css'; import 'uno.css';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
@@ -8,6 +9,8 @@ import styles from './ExtensionRoot.module.scss';
export const styleResetClass = styles.extensionRoot; export const styleResetClass = styles.extensionRoot;
const queryClient = new QueryClient();
/** /**
* A wrapper component for the extension elements that adds some basic styling to them * A wrapper component for the extension elements that adds some basic styling to them
*/ */
@@ -16,7 +19,9 @@ export default function ExtensionRoot(props: React.HTMLProps<HTMLDivElement>): J
return ( return (
<React.StrictMode> <React.StrictMode>
<QueryClientProvider client={queryClient}>
<div className={clsx(styleResetClass, 'h-full', className)} {...others} /> <div className={clsx(styleResetClass, 'h-full', className)} {...others} />
</QueryClientProvider>
</React.StrictMode> </React.StrictMode>
); );
} }