improve theme selection + bug fixes

This commit is contained in:
SethBurkart123
2024-05-29 08:01:44 +10:00
parent 4e5a912cbb
commit 026033ad57
7 changed files with 64 additions and 26 deletions
+4
View File
@@ -56,6 +56,10 @@ html {
background-color: rgb(28 28 30); background-color: rgb(28 28 30);
} }
.timetablepage .days .scrollspacer {
width: 0 !important;
}
.themeCloseButton { .themeCloseButton {
position: absolute !important; position: absolute !important;
color: var(--text-primary) !important; color: var(--text-primary) !important;
+21 -10
View File
@@ -11,7 +11,8 @@ import SpinnerIcon from './LoadingSpinner';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
import useVisibility from './useVisibility'; import useVisibility from './useVisibility';
import { debounce } from 'lodash'; import { debounce, throttle } from 'lodash';
import { Mutex } from '../../seqta/utils/mutex';
interface ThemeSelectorProps { interface ThemeSelectorProps {
isEditMode: boolean; isEditMode: boolean;
@@ -30,6 +31,8 @@ const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> &
threshold: 0.1, // 10% of the element needs to be visible threshold: 0.1, // 10% of the element needs to be visible
}); });
const mutex = new Mutex();
const setSelectedTheme = (themeId: string) => { const setSelectedTheme = (themeId: string) => {
setSettingsState((prevState: SettingsState) => ({ setSettingsState((prevState: SettingsState) => ({
...prevState, ...prevState,
@@ -114,21 +117,29 @@ const ThemeSelector: ForwardRefExoticComponent<Omit<ThemeSelectorProps, "ref"> &
const handleThemeSelect = useCallback( const handleThemeSelect = useCallback(
async (themeId: string) => { async (themeId: string) => {
if (themeId === settingsState.selectedTheme) { const unlock = await mutex.lock();
await disableTheme(); try {
setSelectedTheme(''); if (themeId === settingsState.selectedTheme) {
} else { await disableTheme();
const selectedTheme = themes.find((theme) => theme.id === themeId); setSelectedTheme('');
if (selectedTheme) { } else {
await setTheme(selectedTheme.id); const selectedTheme = themes.find((theme) => theme.id === themeId);
setSelectedTheme(themeId); if (selectedTheme) {
await setTheme(selectedTheme.id);
setSelectedTheme(themeId);
}
} }
} finally {
unlock();
} }
}, },
[settingsState.selectedTheme, themes] [settingsState.selectedTheme, themes]
); );
const handleThemeSelectDebounced = debounce(handleThemeSelect, 50); const handleThemeSelectDebounced = useCallback(
debounce(handleThemeSelect, 100),
[handleThemeSelect]
);
const handleThemeDelete = useCallback( const handleThemeDelete = useCallback(
async (themeId: string) => { async (themeId: string) => {
-8
View File
@@ -1,4 +1,3 @@
import browser from 'webextension-polyfill';
import { CustomImage, CustomTheme } from '../../../interface/types/CustomThemes'; import { CustomImage, CustomTheme } from '../../../interface/types/CustomThemes';
import { applyCustomCSS } from './Themes'; import { applyCustomCSS } from './Themes';
@@ -6,20 +5,13 @@ import { applyCustomCSS } from './Themes';
export const applyTheme = async (theme: CustomTheme) => { export const applyTheme = async (theme: CustomTheme) => {
let CustomCSS = ''; let CustomCSS = '';
let CustomImages: CustomImage[] = []; let CustomImages: CustomImage[] = [];
let defaultColour = '';
if (theme?.CustomCSS) CustomCSS = theme.CustomCSS; if (theme?.CustomCSS) CustomCSS = theme.CustomCSS;
if (theme?.CustomImages) CustomImages = theme.CustomImages; if (theme?.CustomImages) CustomImages = theme.CustomImages;
if (theme?.defaultColour) defaultColour = theme.defaultColour;
// Apply custom CSS // Apply custom CSS
applyCustomCSS(CustomCSS); applyCustomCSS(CustomCSS);
// Apply default color
if (defaultColour !== '') {
browser.storage.local.set({ selectedColor: defaultColour });
}
// Apply custom images // Apply custom images
CustomImages.forEach((image) => { CustomImages.forEach((image) => {
const imageUrl = URL.createObjectURL(image.blob); const imageUrl = URL.createObjectURL(image.blob);
+21 -5
View File
@@ -2,19 +2,35 @@ import browser from 'webextension-polyfill';
import localforage from 'localforage'; import localforage from 'localforage';
import { CustomTheme } from '../../../interface/types/CustomThemes'; import { CustomTheme } from '../../../interface/types/CustomThemes';
import { removeTheme } from './removeTheme'; import { removeTheme } from './removeTheme';
import { Mutex } from '../../utils/mutex';
const mutex = new Mutex();
let isDisabling = false;
export const disableTheme = async () => { 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 { try {
const enabledTheme = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; await browser.storage.local.set({ selectedTheme: '' });
if (enabledTheme.selectedTheme) {
const theme = await localforage.getItem(enabledTheme.selectedTheme) as CustomTheme; if (selectedTheme) {
const theme = await localforage.getItem(selectedTheme) as CustomTheme;
if (theme) { if (theme) {
removeTheme(theme); await removeTheme(theme);
} }
} }
await browser.storage.local.set({ selectedTheme: '' }); await browser.storage.local.set({ selectedTheme: '' });
} catch (error) { } catch (error) {
console.error('Error disabling theme:', error); console.error('Error disabling theme:', error);
} finally {
unlock();
isDisabling = false;
} }
}; };
+3 -3
View File
@@ -5,9 +5,9 @@ import { applyTheme } from './applyTheme';
export const enableCurrentTheme = async () => { export const enableCurrentTheme = async () => {
const themeId = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; }; const { selectedTheme } = await browser.storage.local.get('selectedTheme') as { selectedTheme: string; };
if (themeId.selectedTheme) { if (selectedTheme) {
const theme = await localforage.getItem(themeId.selectedTheme) as CustomTheme; const theme = await localforage.getItem(selectedTheme) as CustomTheme;
if (theme) { if (theme) {
await applyTheme(theme); await applyTheme(theme);
} }
+3
View File
@@ -26,6 +26,9 @@ export const removeTheme = async (theme: CustomTheme) => {
// Remove custom images // Remove custom images
const customImageVariables = theme.CustomImages.map((image) => image.variableName); const customImageVariables = theme.CustomImages.map((image) => image.variableName);
customImageVariables.forEach((variableName) => { customImageVariables.forEach((variableName) => {
const blobUrl = document.documentElement.style.getPropertyValue('--' + variableName);
URL.revokeObjectURL(blobUrl);
document.documentElement.style.removeProperty('--' + variableName); document.documentElement.style.removeProperty('--' + variableName);
}); });
}; };
+12
View File
@@ -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);
}
}