Use custom themes
This commit is contained in:
@@ -57,6 +57,9 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
|
||||
|
||||
// sources
|
||||
'savedSearches',
|
||||
|
||||
// themes
|
||||
'customThemes',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Metadata,
|
||||
MetadataServerSettingKeys,
|
||||
MetadataServerSettings,
|
||||
MetadataThemeSettings,
|
||||
} from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { convertFromGqlMeta, getMetadataFrom, requestUpdateServerMetadata } from '@/lib/metadata/metadata.ts';
|
||||
@@ -50,6 +51,9 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
|
||||
// updates
|
||||
webUIInformAvailableUpdate: true,
|
||||
serverInformAvailableUpdate: true,
|
||||
|
||||
// themes
|
||||
customThemes: {},
|
||||
});
|
||||
|
||||
export const convertSettingsToMetadata = (
|
||||
@@ -57,6 +61,7 @@ export const convertSettingsToMetadata = (
|
||||
): Metadata<string, AllowedMetadataValueTypes> => ({
|
||||
...settings,
|
||||
devices: JSON.stringify(settings.devices),
|
||||
customThemes: JSON.stringify(settings.customThemes),
|
||||
});
|
||||
|
||||
export const convertMetadataToSettings = (
|
||||
@@ -66,6 +71,9 @@ export const convertMetadataToSettings = (
|
||||
...getDefaultSettings(),
|
||||
...(metadata as unknown as MetadataServerSettings),
|
||||
devices: jsonSaveParse<string[]>((metadata.devices as string) ?? '') ?? getDefaultSettings().devices,
|
||||
customThemes:
|
||||
jsonSaveParse<MetadataThemeSettings['customThemes']>((metadata.customThemes as string) ?? '') ??
|
||||
getDefaultSettings().customThemes,
|
||||
}) satisfies MetadataServerSettings;
|
||||
|
||||
const getMetadataServerSettingsWithDefaultFallback = (
|
||||
|
||||
@@ -6,120 +6,165 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { PaletteOptions } from '@mui/material/styles';
|
||||
import { ThemeOptions } from '@mui/material/styles';
|
||||
import { t as translate } from 'i18next';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
|
||||
type Theme = { getName: () => string; palette: PaletteOptions };
|
||||
type Theme = { isCustom: boolean; getName: () => string; muiTheme: ThemeOptions };
|
||||
|
||||
const themes = {
|
||||
default: {
|
||||
isCustom: false,
|
||||
getName: () => translate('global.label.default'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#5b74ef',
|
||||
},
|
||||
secondary: {
|
||||
main: '#efd65b',
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#5b74ef',
|
||||
},
|
||||
secondary: {
|
||||
main: '#efd65b',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
lavender: {
|
||||
getName: () => translate('settings.appearance.themes.lavendar'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#5c58dc',
|
||||
},
|
||||
secondary: {
|
||||
main: '#d8dc58',
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.lavender'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#5c58dc',
|
||||
},
|
||||
secondary: {
|
||||
main: '#d8dc58',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dune: {
|
||||
getName: () => translate('settings.appearance.themes.dune'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#897869',
|
||||
},
|
||||
secondary: {
|
||||
main: '#697a89',
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.dune'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#897869',
|
||||
},
|
||||
secondary: {
|
||||
main: '#697a89',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
rosegold: {
|
||||
getName: () => translate('settings.appearance.themes.rosegold'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#f1a886',
|
||||
},
|
||||
secondary: {
|
||||
main: '#86cff1',
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.rosegold'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#f1a886',
|
||||
},
|
||||
secondary: {
|
||||
main: '#86cff1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
forestDew: {
|
||||
getName: () => translate('settings.appearance.themes.forest_dew'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#53a584',
|
||||
},
|
||||
secondary: {
|
||||
main: '#a55374',
|
||||
'forest dew': {
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.forest_dew'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#53a584',
|
||||
},
|
||||
secondary: {
|
||||
main: '#a55374',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mountainSunset: {
|
||||
getName: () => translate('settings.appearance.themes.mountain_sunset'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#c55a77',
|
||||
},
|
||||
secondary: {
|
||||
main: '#5ac5a8',
|
||||
'montain sunset': {
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.mountain_sunset'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#c55a77',
|
||||
},
|
||||
secondary: {
|
||||
main: '#5ac5a8',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
crimson: {
|
||||
getName: () => translate('settings.appearance.themes.crimson'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#ff0033',
|
||||
},
|
||||
secondary: {
|
||||
main: '#00ffcc',
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.crimson'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#ff0033',
|
||||
},
|
||||
secondary: {
|
||||
main: '#00ffcc',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mintyMiracles: {
|
||||
getName: () => translate('settings.appearance.themes.minty_miracles'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#9defc3',
|
||||
},
|
||||
secondary: {
|
||||
main: '#ef9dc9',
|
||||
'minty miracles': {
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.minty_miracles'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#9defc3',
|
||||
},
|
||||
secondary: {
|
||||
main: '#ef9dc9',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
orangeJuice: {
|
||||
getName: () => translate('settings.appearance.themes.orange_juice'),
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#ffb546',
|
||||
},
|
||||
secondary: {
|
||||
main: '#4690ff',
|
||||
'orange juice': {
|
||||
isCustom: false,
|
||||
getName: () => translate('settings.appearance.theme.themes.orange_juice'),
|
||||
muiTheme: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#ffb546',
|
||||
},
|
||||
secondary: {
|
||||
main: '#4690ff',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const satisfies Record<string, Theme>;
|
||||
|
||||
export type AppThemes = keyof typeof themes;
|
||||
export type AppThemes = keyof typeof themes | string;
|
||||
|
||||
export type AppTheme = Theme & { id: AppThemes };
|
||||
export type AppTheme = Theme & { id: AppThemes; isCustom: boolean };
|
||||
|
||||
export const appThemes = (Object.entries(themes) as [AppThemes, Theme][]).map(([id, theme]) => ({
|
||||
id,
|
||||
...theme,
|
||||
})) satisfies AppTheme[];
|
||||
|
||||
export const getTheme = (id: AppThemes): AppTheme => ({ id, ...themes[id] });
|
||||
export const getTheme = (id: AppThemes, customThemes: Record<string, AppTheme> = {}): AppTheme => {
|
||||
try {
|
||||
const allThemes = { ...themes, ...customThemes };
|
||||
const theme = (allThemes[id as keyof typeof themes] as AppTheme) ?? themes.default;
|
||||
|
||||
return {
|
||||
// @ts-ignore - custom themes do not have a "getName" function
|
||||
getName: () => id,
|
||||
...theme,
|
||||
};
|
||||
} catch (e) {
|
||||
defaultPromiseErrorHandler('getTheme')(e);
|
||||
}
|
||||
return { id, ...themes[id as keyof typeof themes] };
|
||||
};
|
||||
|
||||
export const isThemeNameUnique = (id: string, customThemes: Record<string, AppTheme>): boolean =>
|
||||
Object.keys({ ...themes, ...customThemes }).every((themeId) => themeId.toLowerCase() !== id.toLowerCase());
|
||||
|
||||
Reference in New Issue
Block a user