Add the Theme Switcher storage and options functionality.
This commit is contained in:
parent
893a2173f3
commit
9d5f0d1afc
|
@ -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";
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue