Fix theme shop themes not applying as a base

- [#217] fixed theme shop themes not applying as a base
- added `themes` state path to store themes locally at
This commit is contained in:
brecert 2021-09-11 15:40:14 -04:00
parent 5b422b89e3
commit 537f9b1596
No known key found for this signature in database
GPG key ID: 1B2E56B9EC985B96
6 changed files with 86 additions and 18 deletions

View file

@ -7,6 +7,8 @@ import { useEffect } from "preact/hooks";
import { connectState } from "../redux/connector";
import { Children } from "../types/Preact";
import { fetchManifest, fetchTheme } from "../pages/settings/panes/ThemeShop";
import { getState } from "../redux";
export type Variables =
| "accent"
@ -72,7 +74,7 @@ export type Theme = {
};
export interface ThemeOptions {
preset?: string;
base?: string;
ligatures?: boolean;
custom?: Partial<Theme>;
}
@ -275,6 +277,27 @@ export const PRESETS: Record<string, Theme> = {
},
};
// todo: store used themes locally
export function getBaseTheme(name: string): Theme {
if (name in PRESETS) {
return PRESETS[name]
}
const themes = getState().themes
if (name in themes) {
const { theme } = themes[name];
return {
...PRESETS[theme.light ? 'light' : 'dark'],
...theme
}
}
// how did we get here
return PRESETS['dark']
}
const keys = Object.keys(PRESETS.dark);
const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
:root {
@ -283,10 +306,9 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
`;
export const generateVariables = (theme: Theme) => {
const mergedTheme = { ...PRESETS[theme.light ? 'light' : 'dark'], ...theme }
return (Object.keys(mergedTheme) as Variables[]).map((key) => {
return (Object.keys(theme) as Variables[]).map((key) => {
if (!keys.includes(key)) return;
return `--${key}: ${mergedTheme[key]};`;
return `--${key}: ${theme[key]};`;
})
}
@ -300,8 +322,7 @@ interface Props {
function Theme({ children, options }: Props) {
const theme: Theme = {
...PRESETS["dark"],
...PRESETS[options?.preset ?? ""],
...getBaseTheme(options?.base ?? 'dark'),
...options?.custom,
};

View file

@ -98,7 +98,7 @@ export function Component(props: Props) {
useEffect(() => setOverride({ css }), [setOverride, css]);
const selected = props.settings.theme?.preset ?? "dark";
const selected = props.settings.theme?.base ?? "dark";
return (
<div className={styles.appearance}>
<h3>
@ -113,7 +113,7 @@ export function Component(props: Props) {
data-active={selected === "light"}
onClick={() =>
selected !== "light" &&
setTheme({ preset: "light" })
setTheme({ base: "light" })
}
onContextMenu={(e) => e.preventDefault()}
/>
@ -128,7 +128,7 @@ export function Component(props: Props) {
draggable={false}
data-active={selected === "dark"}
onClick={() =>
selected !== "dark" && setTheme({ preset: "dark" })
selected !== "dark" && setTheme({ base: "dark" })
}
onContextMenu={(e) => e.preventDefault()}
/>

View file

@ -4,7 +4,7 @@ import { useEffect, useState } from "preact/hooks";
import { dispatch } from "../../../redux";
import { Theme, generateVariables } from "../../../context/Theme";
import { Theme, generateVariables, ThemeOptions } from "../../../context/Theme";
import Tip from "../../../components/ui/Tip";
import previewPath from "../assets/preview.svg";
@ -21,14 +21,14 @@ export const fetchTheme = (slug: string): Promise<Theme> =>
res.json(),
);
interface ThemeMetadata {
export interface ThemeMetadata {
name: string;
creator: string;
commit?: string;
description: string;
}
type Manifest = {
export type Manifest = {
generated: string;
themes: Record<string, ThemeMetadata>;
};
@ -189,14 +189,21 @@ export function ThemeShop() {
<div class="description">{theme.description}</div>
<button
class="preview"
onClick={() =>
onClick={() => {
dispatch({
type: "THEMES_SET_THEME",
theme: {
slug,
meta: theme,
theme: themeData[slug]
}
})
dispatch({
type: "SETTINGS_SET_THEME",
theme: {
custom: themeData[slug],
},
})
}>
theme: { base: slug },
});
}}>
<ThemePreview slug={slug} theme={themeData[slug]} />
</button>
</ThemeInfo>

View file

@ -14,6 +14,7 @@ import { QueuedMessage } from "./reducers/queue";
import { SectionToggle } from "./reducers/section_toggle";
import { Settings } from "./reducers/settings";
import { SyncOptions } from "./reducers/sync";
import { Themes } from "./reducers/themes";
import { TrustedLinks } from "./reducers/trusted_links";
import { Unreads } from "./reducers/unreads";
@ -31,6 +32,7 @@ export type State = {
notifications: Notifications;
sectionToggle: SectionToggle;
trustedLinks: TrustedLinks;
themes: Themes;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -62,6 +64,7 @@ store.subscribe(() => {
notifications,
sectionToggle,
trustedLinks,
themes,
} = store.getState() as State;
localForage.setItem("state", {
@ -78,6 +81,7 @@ store.subscribe(() => {
notifications,
sectionToggle,
trustedLinks,
themes,
});
});

View file

@ -12,6 +12,7 @@ import { sectionToggle, SectionToggleAction } from "./section_toggle";
import { config, ConfigAction } from "./server_config";
import { settings, SettingsAction } from "./settings";
import { sync, SyncAction } from "./sync";
import { themes, ThemesAction } from "./themes";
import { trustedLinks, TrustedLinksAction } from "./trusted_links";
import { unreads, UnreadsAction } from "./unreads";
@ -29,6 +30,7 @@ export default combineReducers({
notifications,
sectionToggle,
trustedLinks,
themes,
});
export type Action =
@ -45,4 +47,5 @@ export type Action =
| NotificationsAction
| SectionToggleAction
| TrustedLinksAction
| ThemesAction
| { type: "__INIT"; state: State };

View file

@ -0,0 +1,33 @@
import { Theme } from "../../context/Theme";
import { ThemeMetadata } from "../../pages/settings/panes/ThemeShop";
export interface StoredTheme {
slug: string;
meta: ThemeMetadata;
theme: Theme;
}
export type Themes = Record<string, StoredTheme>;
export type ThemesAction =
| { type: undefined }
| { type: "THEMES_SET_THEME"; theme: StoredTheme }
| { type: "THEMES_REMOVE_THEME"; slug: string }
| { type: "RESET" };
export function themes(state: Themes = {}, action: ThemesAction) {
switch (action.type) {
case "THEMES_SET_THEME":
return {
...state,
[action.theme.slug]: action.theme,
};
case "THEMES_REMOVE_THEME":
return { ...state, [action.slug]: null };
case "RESET":
return {};
default:
return state;
}
}