mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
improve theme selection + bug fixes
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user