From 026033ad5700ae9e062cf81c3ced3503304c6705 Mon Sep 17 00:00:00 2001 From: SethBurkart123 Date: Wed, 29 May 2024 08:01:44 +1000 Subject: [PATCH] improve theme selection + bug fixes --- src/css/injected.scss | 4 +++ src/interface/components/ThemeSelector.tsx | 31 +++++++++++++++------- src/seqta/ui/themes/applyTheme.ts | 8 ------ src/seqta/ui/themes/disableTheme.ts | 26 ++++++++++++++---- src/seqta/ui/themes/enableCurrent.ts | 6 ++--- src/seqta/ui/themes/removeTheme.ts | 3 +++ src/seqta/utils/mutex.ts | 12 +++++++++ 7 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 src/seqta/utils/mutex.ts diff --git a/src/css/injected.scss b/src/css/injected.scss index c8c6edb9..e6e499a6 100644 --- a/src/css/injected.scss +++ b/src/css/injected.scss @@ -56,6 +56,10 @@ html { background-color: rgb(28 28 30); } +.timetablepage .days .scrollspacer { + width: 0 !important; +} + .themeCloseButton { position: absolute !important; color: var(--text-primary) !important; diff --git a/src/interface/components/ThemeSelector.tsx b/src/interface/components/ThemeSelector.tsx index e6995c88..978d38c9 100644 --- a/src/interface/components/ThemeSelector.tsx +++ b/src/interface/components/ThemeSelector.tsx @@ -11,7 +11,8 @@ import SpinnerIcon from './LoadingSpinner'; import { toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import useVisibility from './useVisibility'; -import { debounce } from 'lodash'; +import { debounce, throttle } from 'lodash'; +import { Mutex } from '../../seqta/utils/mutex'; interface ThemeSelectorProps { isEditMode: boolean; @@ -30,6 +31,8 @@ const ThemeSelector: ForwardRefExoticComponent & threshold: 0.1, // 10% of the element needs to be visible }); + const mutex = new Mutex(); + const setSelectedTheme = (themeId: string) => { setSettingsState((prevState: SettingsState) => ({ ...prevState, @@ -114,21 +117,29 @@ const ThemeSelector: ForwardRefExoticComponent & const handleThemeSelect = useCallback( async (themeId: string) => { - if (themeId === settingsState.selectedTheme) { - await disableTheme(); - setSelectedTheme(''); - } else { - const selectedTheme = themes.find((theme) => theme.id === themeId); - if (selectedTheme) { - await setTheme(selectedTheme.id); - setSelectedTheme(themeId); + const unlock = await mutex.lock(); + try { + if (themeId === settingsState.selectedTheme) { + await disableTheme(); + setSelectedTheme(''); + } else { + const selectedTheme = themes.find((theme) => theme.id === themeId); + if (selectedTheme) { + await setTheme(selectedTheme.id); + setSelectedTheme(themeId); + } } + } finally { + unlock(); } }, [settingsState.selectedTheme, themes] ); - const handleThemeSelectDebounced = debounce(handleThemeSelect, 50); + const handleThemeSelectDebounced = useCallback( + debounce(handleThemeSelect, 100), + [handleThemeSelect] + ); const handleThemeDelete = useCallback( async (themeId: string) => { diff --git a/src/seqta/ui/themes/applyTheme.ts b/src/seqta/ui/themes/applyTheme.ts index a00fbded..314a3826 100644 --- a/src/seqta/ui/themes/applyTheme.ts +++ b/src/seqta/ui/themes/applyTheme.ts @@ -1,4 +1,3 @@ -import browser from 'webextension-polyfill'; import { CustomImage, CustomTheme } from '../../../interface/types/CustomThemes'; import { applyCustomCSS } from './Themes'; @@ -6,20 +5,13 @@ import { applyCustomCSS } from './Themes'; export const applyTheme = async (theme: CustomTheme) => { let CustomCSS = ''; let CustomImages: CustomImage[] = []; - let defaultColour = ''; if (theme?.CustomCSS) CustomCSS = theme.CustomCSS; if (theme?.CustomImages) CustomImages = theme.CustomImages; - if (theme?.defaultColour) defaultColour = theme.defaultColour; // Apply custom CSS applyCustomCSS(CustomCSS); - // Apply default color - if (defaultColour !== '') { - browser.storage.local.set({ selectedColor: defaultColour }); - } - // Apply custom images CustomImages.forEach((image) => { const imageUrl = URL.createObjectURL(image.blob); diff --git a/src/seqta/ui/themes/disableTheme.ts b/src/seqta/ui/themes/disableTheme.ts index 5a7c4d8e..0576c391 100644 --- a/src/seqta/ui/themes/disableTheme.ts +++ b/src/seqta/ui/themes/disableTheme.ts @@ -2,19 +2,35 @@ import browser from 'webextension-polyfill'; import localforage from 'localforage'; import { CustomTheme } from '../../../interface/types/CustomThemes'; import { removeTheme } from './removeTheme'; +import { Mutex } from '../../utils/mutex'; +const mutex = new Mutex(); +let isDisabling = false; export const disableTheme = async () => { + if (isDisabling) return; + + const { selectedTheme } = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; + if (!selectedTheme || selectedTheme === '') { + // Theme is already disabled, exit early + return; + } + isDisabling = true; + const unlock = await mutex.lock(); try { - const enabledTheme = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; - if (enabledTheme.selectedTheme) { - const theme = await localforage.getItem(enabledTheme.selectedTheme) as CustomTheme; + await browser.storage.local.set({ selectedTheme: '' }); + + if (selectedTheme) { + const theme = await localforage.getItem(selectedTheme) as CustomTheme; if (theme) { - removeTheme(theme); + await removeTheme(theme); } } await browser.storage.local.set({ selectedTheme: '' }); } catch (error) { console.error('Error disabling theme:', error); + } finally { + unlock(); + isDisabling = false; } -}; +}; \ No newline at end of file diff --git a/src/seqta/ui/themes/enableCurrent.ts b/src/seqta/ui/themes/enableCurrent.ts index 7274bdfc..ead51e20 100644 --- a/src/seqta/ui/themes/enableCurrent.ts +++ b/src/seqta/ui/themes/enableCurrent.ts @@ -5,9 +5,9 @@ import { applyTheme } from './applyTheme'; export const enableCurrentTheme = async () => { - const themeId = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; - if (themeId.selectedTheme) { - const theme = await localforage.getItem(themeId.selectedTheme) as CustomTheme; + const { selectedTheme } = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; + if (selectedTheme) { + const theme = await localforage.getItem(selectedTheme) as CustomTheme; if (theme) { await applyTheme(theme); } diff --git a/src/seqta/ui/themes/removeTheme.ts b/src/seqta/ui/themes/removeTheme.ts index 56a4c671..650888d9 100644 --- a/src/seqta/ui/themes/removeTheme.ts +++ b/src/seqta/ui/themes/removeTheme.ts @@ -26,6 +26,9 @@ export const removeTheme = async (theme: CustomTheme) => { // Remove custom images const customImageVariables = theme.CustomImages.map((image) => image.variableName); customImageVariables.forEach((variableName) => { + const blobUrl = document.documentElement.style.getPropertyValue('--' + variableName); + URL.revokeObjectURL(blobUrl); + document.documentElement.style.removeProperty('--' + variableName); }); }; diff --git a/src/seqta/utils/mutex.ts b/src/seqta/utils/mutex.ts new file mode 100644 index 00000000..d04cbf13 --- /dev/null +++ b/src/seqta/utils/mutex.ts @@ -0,0 +1,12 @@ +// Simple mutex implementation +export class Mutex { + private mutex = Promise.resolve(); + + lock(): PromiseLike<() => void> { + let begin: (unlock: () => void) => void; + + this.mutex = this.mutex.then(() => new Promise(begin)); + + return new Promise(res => begin = res); + } +} \ No newline at end of file