mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-25 23:22:06 -05:00
Merge pull request #225 from brecert/theme_shop
Fix theme shop themes not applying as a base
This commit is contained in:
commit
fc03336ea3
6 changed files with 86 additions and 18 deletions
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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()}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
33
src/redux/reducers/themes.ts
Normal file
33
src/redux/reducers/themes.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue