1
Fork 0

Add the Theme Switcher storage and options functionality.

This commit is contained in:
Bauke 2023-12-19 15:18:34 +01:00
parent 893a2173f3
commit 9d5f0d1afc
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
7 changed files with 174 additions and 1 deletions

View File

@ -8,5 +8,6 @@ export {JumpToNewCommentSetting} from "./jump-to-new-comment.js";
export {MarkdownToolbarSetting} from "./markdown-toolbar.js"; export {MarkdownToolbarSetting} from "./markdown-toolbar.js";
export {MiscellaneousSetting} from "./miscellaneous.js"; export {MiscellaneousSetting} from "./miscellaneous.js";
export {ThemedLogoSetting} from "./themed-logo.js"; export {ThemedLogoSetting} from "./themed-logo.js";
export {ThemeSwitcherSetting} from "./theme-switcher.js";
export {UserLabelsSetting} from "./user-labels.js"; export {UserLabelsSetting} from "./user-labels.js";
export {UsernameColorsSetting} from "./username-colors.js"; export {UsernameColorsSetting} from "./username-colors.js";

View File

@ -0,0 +1,129 @@
import {Component} from "preact";
import {type Value} from "@holllo/webextension-storage";
import {
Data,
Feature,
fromStorage,
type ThemeSwitcherData,
} from "../../storage/exports.js";
import {Setting, type SettingProps} from "./index.js";
type State = {
data: Value<ThemeSwitcherData> | undefined;
hasUnsavedChanges: boolean;
themesList: Array<[string, string]>;
};
export class ThemeSwitcherSetting extends Component<SettingProps, State> {
constructor(props: SettingProps) {
super(props);
this.state = {
data: undefined,
hasUnsavedChanges: false,
themesList: [],
};
}
async componentDidMount() {
const themesList = await fromStorage(Data.ThemesList);
this.setState({
data: await fromStorage(Feature.ThemeSwitcher),
themesList: themesList.value,
});
}
onChange = (event: Event, key: keyof ThemeSwitcherData) => {
const {data} = this.state;
const target = event.target as HTMLInputElement | HTMLSelectElement;
if (data === undefined) {
return;
}
data.value[key] = target.value;
this.setState({data, hasUnsavedChanges: true});
};
save = async () => {
const {data} = this.state;
if (data === undefined) {
return;
}
await data.save();
this.setState({hasUnsavedChanges: false});
};
render() {
const {data, hasUnsavedChanges, themesList} = this.state;
if (data === undefined) {
return;
}
const themeOptions = themesList.map(([value, text]) => (
<option value={value}>{text}</option>
));
const unsavedChanges = hasUnsavedChanges ? "unsaved-changes" : "";
return (
<Setting {...this.props}>
<p class="info">
Automatically switch between two themes at certain times of the day.
</p>
<button
class={`button margin-bottom-8 has-save-status ${unsavedChanges}`}
onClick={this.save}
>
Save{hasUnsavedChanges ? "*" : ""}
</button>
<p>
Switch to{" "}
<select
class="styled-select"
onChange={(event) => {
this.onChange(event, "themeA");
}}
value={data.value.themeA}
>
{themeOptions}
</select>{" "}
at{" "}
<input
class="styled-text-input"
onChange={(event) => {
this.onChange(event, "hourA");
}}
type="text"
value={data.value.hourA}
/>
</p>
<p>
Switch to{" "}
<select
class="styled-select"
onChange={(event) => {
this.onChange(event, "themeB");
}}
value={data.value.themeB}
>
{themeOptions}
</select>{" "}
at{" "}
<input
class="styled-text-input"
onChange={(event) => {
this.onChange(event, "hourB");
}}
type="text"
value={data.value.hourB}
/>
</p>
</Setting>
);
}
}

View File

@ -9,6 +9,7 @@ import {
JumpToNewCommentSetting, JumpToNewCommentSetting,
MarkdownToolbarSetting, MarkdownToolbarSetting,
MiscellaneousSetting, MiscellaneousSetting,
ThemeSwitcherSetting,
ThemedLogoSetting, ThemedLogoSetting,
UserLabelsSetting, UserLabelsSetting,
UsernameColorsSetting, UsernameColorsSetting,
@ -83,6 +84,13 @@ export const features: FeatureData[] = [
title: "Miscellaneous", title: "Miscellaneous",
component: MiscellaneousSetting, component: MiscellaneousSetting,
}, },
{
availableSince: new Date("2023-12-24"),
index: 0,
key: Feature.ThemeSwitcher,
title: "Theme Switcher",
component: ThemeSwitcherSetting,
},
{ {
availableSince: new Date("2022-02-27"), availableSince: new Date("2022-02-27"),
index: 0, index: 0,

View File

@ -1,5 +1,5 @@
@mixin button { @mixin button {
--button-color: var(--blue); --button-color: var(--save-status-color, var(--blue));
--button-color-alt: var(--dark-blue); --button-color-alt: var(--dark-blue);
background-color: var(--button-color); background-color: var(--button-color);

View File

@ -157,10 +157,15 @@
} }
} }
.styled-text-input,
.styled-select { .styled-select {
background-color: var(--background-primary); background-color: var(--background-primary);
border: 1px solid var(--save-status-color, var(--blue)); border: 1px solid var(--save-status-color, var(--blue));
color: var(--foreground); color: var(--foreground);
padding: 8px; padding: 8px;
} }
.margin-bottom-8 {
margin-bottom: 8px;
}
} }

View File

@ -12,6 +12,7 @@ export enum Feature {
MarkdownToolbar = "markdown-toolbar", MarkdownToolbar = "markdown-toolbar",
Miscellaneous = "miscellaneous-features", Miscellaneous = "miscellaneous-features",
ThemedLogo = "themed-logo", ThemedLogo = "themed-logo",
ThemeSwitcher = "theme-switcher",
UserLabels = "user-labels", UserLabels = "user-labels",
UsernameColors = "username-colors", UsernameColors = "username-colors",
} }

View File

@ -27,6 +27,23 @@ export type HideVotesData = {
ownTopics: boolean; ownTopics: boolean;
}; };
/**
* The data stored for the Theme Switcher feature.
*/
export type ThemeSwitcherData = {
/** The hour to switch to theme A. */
hourA: string;
/** The hour to switch to theme B. */
hourB: string;
/** The value of the theme from the theme selector for the first theme. */
themeA: string;
/** The value of the theme from the theme selector for the second theme. */
themeB: string;
};
/** /**
* All storage {@link Value}s stored in WebExtension storage. * All storage {@link Value}s stored in WebExtension storage.
*/ */
@ -134,6 +151,18 @@ export const storageValues = {
}, },
storage: browser.storage.sync, storage: browser.storage.sync,
}), }),
[Feature.ThemeSwitcher]: createValue<ThemeSwitcherData>({
deserialize: (input) => JSON.parse(input) as ThemeSwitcherData,
serialize: (input) => JSON.stringify(input),
key: Feature.ThemeSwitcher,
value: {
hourA: "09:00",
hourB: "21:00",
themeA: "white",
themeB: "black",
},
storage: browser.storage.sync,
}),
[Feature.UserLabels]: collectUserLabels(), [Feature.UserLabels]: collectUserLabels(),
[Feature.UsernameColors]: collectUsernameColors(), [Feature.UsernameColors]: collectUsernameColors(),
}; };