1
Fork 0
tildes-reextended/source/options/components/username-colors.tsx

258 lines
6.7 KiB
TypeScript

import {type Value} from "@holllo/webextension-storage";
import {Component} from "preact";
import {log} from "../../utilities/exports.js";
import {
type UsernameColorsData,
type UsernameColor,
Feature,
Data,
createValueUsernamecolor,
fromStorage,
} from "../../storage/exports.js";
import {Setting, type SettingProps} from "./index.js";
type State = {
previewChecked: "off" | "foreground" | "background";
usernameColors: UsernameColorsData;
usernameColorsToRemove: UsernameColorsData;
randomizeChecked: Value<boolean>;
unsavedUsernameColorIds: Set<number>;
};
export class UsernameColorsSetting extends Component<SettingProps, State> {
constructor(props: SettingProps) {
super(props);
this.state = {
previewChecked: "off",
usernameColors: undefined!,
usernameColorsToRemove: [],
randomizeChecked: undefined!,
unsavedUsernameColorIds: new Set(),
};
}
async componentDidMount() {
this.setState({
randomizeChecked: await fromStorage(Data.RandomizeUsernameColors),
usernameColors: await fromStorage(Feature.UsernameColors),
});
}
addNewColor = async () => {
const {usernameColors, unsavedUsernameColorIds} = this.state;
let id = 1;
if (usernameColors.length > 0) {
id =
usernameColors.sort((a, b) => b.value.id - a.value.id)[0].value.id + 1;
}
const newColor = await createValueUsernamecolor({
color: "",
id,
username: "",
});
unsavedUsernameColorIds.add(id);
this.state.usernameColors.push(newColor);
this.setState({
usernameColors,
unsavedUsernameColorIds,
});
};
removeColor = async (targetId: number) => {
const {usernameColors, usernameColorsToRemove, unsavedUsernameColorIds} =
this.state;
const targetIndex = usernameColors.findIndex(
({value}) => value.id === targetId,
);
if (targetIndex === -1) {
log(`Tried to remove unknown UsernameColor ID: ${targetId}`);
return;
}
usernameColorsToRemove.push(...usernameColors.splice(targetIndex, 1));
unsavedUsernameColorIds.add(targetId);
this.setState({
usernameColors,
usernameColorsToRemove,
unsavedUsernameColorIds,
});
};
saveChanges = async () => {
for (const usernameColor of this.state.usernameColorsToRemove) {
await usernameColor.remove();
}
for (const usernameColor of this.state.usernameColors) {
await usernameColor.save();
}
this.setState({
usernameColorsToRemove: [],
unsavedUsernameColorIds: new Set(),
});
};
togglePreview = async () => {
let {previewChecked} = this.state;
// eslint-disable-next-line default-case
switch (previewChecked) {
case "off": {
previewChecked = "foreground";
break;
}
case "foreground": {
previewChecked = "background";
break;
}
case "background": {
previewChecked = "off";
break;
}
}
this.setState({previewChecked});
};
toggleRandomized = async () => {
const randomizeChecked = this.state.randomizeChecked;
randomizeChecked.value = !randomizeChecked.value;
await randomizeChecked.save();
this.setState({randomizeChecked});
};
onInput = (event: Event, id: number, key: "color" | "username") => {
const {unsavedUsernameColorIds} = this.state;
const colorIndex = this.state.usernameColors.findIndex(
({value}) => value.id === id,
);
if (colorIndex === -1) {
log(`Tried to edit unknown UsernameColor ID: ${id}`);
return;
}
const newValue = (event.target as HTMLInputElement).value;
this.state.usernameColors[colorIndex].value[key] = newValue;
unsavedUsernameColorIds.add(id);
this.setState({usernameColors: this.state.usernameColors});
};
render() {
const {
previewChecked,
usernameColors,
randomizeChecked,
unsavedUsernameColorIds,
} = this.state;
if (usernameColors === undefined) {
return;
}
usernameColors.sort((a, b) => a.value.id - b.value.id);
const editors = usernameColors.map(({value: {color, id, username}}) => {
const style: Record<string, string> = {};
if (previewChecked === "background") {
style.backgroundColor = color;
} else if (previewChecked === "foreground") {
style.color = color;
}
const usernameHandler = (event: Event) => {
this.onInput(event, id, "username");
};
const colorHandler = (event: Event) => {
this.onInput(event, id, "color");
};
const removeHandler = async () => {
await this.removeColor(id);
};
const hasUnsavedChanges = unsavedUsernameColorIds.has(id)
? "unsaved-changes"
: "";
return (
<div
class={`has-save-status username-colors-editor ${hasUnsavedChanges}`}
key={id}
>
<input
style={style}
placeholder="Username(s)"
value={username}
onInput={usernameHandler}
/>
<input
style={style}
placeholder="Color"
value={color}
onInput={colorHandler}
/>
<button class="button destructive" onClick={removeHandler}>
Remove
</button>
</div>
);
});
const anyUnsavedChanges =
unsavedUsernameColorIds.size > 0 ? "unsaved-changes" : "";
return (
<Setting {...this.props}>
<p class="info">
Assign custom colors to usernames.
<br />
You can enter multiple usernames separated by a comma if you want them
to use the same color.
<br />
If randomize is enabled then all usernames will be given a random
background color based on a hash of the username. Manually assigned
colors will be applied normally.
</p>
<div class="username-colors-controls">
<button class="button" onClick={this.addNewColor}>
Add New Color
</button>
<button class="button" onClick={this.togglePreview}>
Toggle Preview
</button>
<button
class={`button save-button ${anyUnsavedChanges}`}
onClick={this.saveChanges}
>
Save Changes{anyUnsavedChanges.length > 0 ? "*" : ""}
</button>
<ul class="checkbox-list">
<li>
<label class="styled-checkbox">
<input
type="checkbox"
checked={randomizeChecked.value}
onClick={this.toggleRandomized}
/>
Randomize Username Colors
</label>
</li>
</ul>
</div>
{editors}
</Setting>
);
}
}