Merge pull request #225 from brecert/theme_shop

Fix theme shop themes not applying as a base
This commit is contained in:
Paul Makles 2021-09-14 20:39:28 +01:00 committed by GitHub
commit fc03336ea3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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 { connectState } from "../redux/connector";
import { Children } from "../types/Preact"; import { Children } from "../types/Preact";
import { fetchManifest, fetchTheme } from "../pages/settings/panes/ThemeShop";
import { getState } from "../redux";
export type Variables = export type Variables =
| "accent" | "accent"
@ -72,7 +74,7 @@ export type Theme = {
}; };
export interface ThemeOptions { export interface ThemeOptions {
preset?: string; base?: string;
ligatures?: boolean; ligatures?: boolean;
custom?: Partial<Theme>; 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 keys = Object.keys(PRESETS.dark);
const GlobalTheme = createGlobalStyle<{ theme: Theme }>` const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
:root { :root {
@ -283,10 +306,9 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
`; `;
export const generateVariables = (theme: Theme) => { export const generateVariables = (theme: Theme) => {
const mergedTheme = { ...PRESETS[theme.light ? 'light' : 'dark'], ...theme } return (Object.keys(theme) as Variables[]).map((key) => {
return (Object.keys(mergedTheme) as Variables[]).map((key) => {
if (!keys.includes(key)) return; if (!keys.includes(key)) return;
return `--${key}: ${mergedTheme[key]};`; return `--${key}: ${theme[key]};`;
}) })
} }
@ -300,8 +322,7 @@ interface Props {
function Theme({ children, options }: Props) { function Theme({ children, options }: Props) {
const theme: Theme = { const theme: Theme = {
...PRESETS["dark"], ...getBaseTheme(options?.base ?? 'dark'),
...PRESETS[options?.preset ?? ""],
...options?.custom, ...options?.custom,
}; };

View file

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

View file

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

View file

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

View file

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