feat(mobx): rewrite appearance menu

This commit is contained in:
Paul
2021-12-15 18:23:05 +00:00
parent 65be047dc6
commit c7df0088fc
19 changed files with 558 additions and 403 deletions

View File

@@ -1,231 +1,43 @@
import { Reset, Import } from "@styled-icons/boxicons-regular";
import { Pencil } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
// @ts-expect-error shade-blend-color does not have typings.
import pSBC from "shade-blend-color";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useCallback, useEffect, useState } from "preact/hooks";
import { debounce } from "../../../lib/debounce";
import { useApplicationState } from "../../../mobx/State";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
import { Theme, ThemeOptions } from "../../../context/Theme";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import CollapsibleSection from "../../../components/common/CollapsibleSection";
import Tooltip from "../../../components/common/Tooltip";
import Button from "../../../components/ui/Button";
import InputBox from "../../../components/ui/InputBox";
import {
ThemeBaseSelectorShim,
ThemeShopShim,
ThemeAccentShim,
DisplayCompactShim,
DisplayFontShim,
DisplayMonospaceFontShim,
DisplayLigaturesShim,
DisplayEmojiShim,
ThemeCustomCSSShim,
} from "../../../components/settings/AppearanceShims";
import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides";
import ThemeTools from "../../../components/settings/appearance/ThemeTools";
interface Props {
settings: Settings;
}
// ! FIXME: code needs to be rewritten to fix jittering
export const Component = observer((props: Props) => {
//const theme = useApplicationState().settings.theme;
const { writeClipboard, openScreen } = useIntermediate();
/*function setTheme(theme: ThemeOptions) {
dispatch({
type: "SETTINGS_SET_THEME",
theme,
});
}
const pushOverride = useCallback((custom: Partial<Theme>) => {
dispatch({
type: "SETTINGS_SET_THEME_OVERRIDE",
custom,
});
}, []);
function setAccent(accent: string) {
setOverride({
accent,
"scrollbar-thumb": pSBC(-0.2, accent),
});
}
const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
function setEmojiPack(emojiPack: EmojiPacks) {
dispatch({
type: "SETTINGS_SET_APPEARANCE",
options: {
emojiPack,
},
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const setOverride = useCallback(
debounce(pushOverride as (...args: unknown[]) => void, 200),
[pushOverride],
) as (custom: Partial<Theme>) => void;
const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
useEffect(() => setOverride({ css }), [setOverride, css]);
const selected = theme.getBase();*/
export const Appearance = observer(() => {
return (
<div className={styles.appearance}>
<ThemeBaseSelectorShim />
<ThemeShopShim />
<ThemeAccentShim />
{/*<DisplayCompactShim />
<DisplayFontShim />
<DisplayLigaturesShim />
<DisplayEmojiShim />*/}
<DisplayEmojiShim />
{/*<CollapsibleSection
<CollapsibleSection
defaultValue={false}
id="settings_overrides"
summary={<Text id="app.settings.pages.appearance.overrides" />}>
<div className={styles.actions}>
<Tooltip
content={
<Text id="app.settings.pages.appearance.reset_overrides" />
}>
<Button
contrast
iconbutton
onClick={() => setTheme({ custom: {} })}>
<Reset size={22} />
</Button>
</Tooltip>
<div
className={styles.code}
onClick={() => writeClipboard(JSON.stringify(theme))}>
<Tooltip content={<Text id="app.special.copy" />}>
{" "}
{JSON.stringify(theme)}
</Tooltip>
</div>
<Tooltip
content={
<Text id="app.settings.pages.appearance.import" />
}>
<Button
contrast
iconbutton
onClick={async () => {
try {
const text =
await navigator.clipboard.readText();
setOverride(JSON.parse(text));
} catch (err) {
openScreen({
id: "_input",
question: (
<Text id="app.settings.pages.appearance.import_theme" />
),
field: (
<Text id="app.settings.pages.appearance.theme_data" />
),
callback: async (string) =>
setOverride(JSON.parse(string)),
});
}
}}>
<Import size={22} />
</Button>
</Tooltip>
</div>
<ThemeTools />
<h3>App</h3>
<div className={styles.overrides}>
{(
[
"accent",
"background",
"foreground",
"primary-background",
"primary-header",
"secondary-background",
"secondary-foreground",
"secondary-header",
"tertiary-background",
"tertiary-foreground",
"block",
"message-box",
"mention",
"scrollbar-thumb",
"scrollbar-track",
"status-online",
"status-away",
"status-busy",
"status-streaming",
"status-invisible",
"success",
"warning",
"error",
"hover",
] as const
).map((x) => (
<div
className={styles.entry}
key={x}
style={{ backgroundColor: theme.getVariable(x) }}>
<div className={styles.input}>
<input
type="color"
value={theme.getVariable(x)}
onChange={(v) =>
setOverride({
[x]: v.currentTarget.value,
})
}
/>
</div>
<span
style={`color: ${getContrastingColour(
theme.getVariable(x),
theme.getVariable("primary-background"),
)}`}>
{x}
</span>
<div className={styles.override}>
<div
className={styles.picker}
onClick={(e) =>
e.currentTarget.parentElement?.parentElement
?.querySelector("input")
?.click()
}>
<Pencil size={24} />
</div>
<InputBox
type="text"
className={styles.text}
value={theme.getVariable(x)}
onChange={(y) =>
setOverride({
[x]: y.currentTarget.value,
})
}
/>
</div>
</div>
))}
</div>
</CollapsibleSection>*/}
<ThemeOverrides />
</CollapsibleSection>
<CollapsibleSection
id="settings_advanced_appearance"
@@ -238,19 +50,4 @@ export const Component = observer((props: Props) => {
);
});
export const Appearance = connectState(Component, (state) => {
return {
settings: state.settings,
};
});
function getContrastingColour(hex: string, fallback: string): string {
hex = hex.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const cc = (r * 299 + g * 587 + b * 114) / 1000;
if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(cc))
return getContrastingColour(fallback, "#fffff");
return cc >= 175 ? "black" : "white";
}
// <DisplayCompactShim />

View File

@@ -15,6 +15,8 @@ import Tip from "../../../components/ui/Tip";
const constraints = { audio: true };
// TODO: do not rewrite this code until voice is rewritten!
export function Component() {
const [mediaStream, setMediaStream] = useState<MediaStream | undefined>(
undefined,
@@ -57,11 +59,11 @@ export function Component() {
return () => {
if (mediaStream) {
// close microphone access on unmount
mediaStream.getTracks().forEach(track => {
track.stop()
})
mediaStream.getTracks().forEach((track) => {
track.stop();
});
}
}
};
}, [mediaStream]);
useEffect(() => {

View File

@@ -232,97 +232,6 @@
display: flex;
flex-direction: column;
}
.actions {
gap: 8px;
display: flex;
margin: 18px 0 8px 0;
.code {
cursor: pointer;
display: flex;
align-items: center;
font-size: 0.875rem;
min-width: 0;
flex-grow: 1;
padding: 8px;
font-family: var(--monospace-font);
border-radius: var(--border-radius);
background: var(--secondary-background);
> div {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.overrides {
row-gap: 8px;
display: grid;
column-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
margin-bottom: 20px;
.entry {
padding: 12px;
margin-top: 8px;
border: 1px solid black;
border-radius: var(--border-radius);
span {
flex: 1;
display: block;
font-weight: 600;
font-size: 0.875rem;
margin-bottom: 8px;
text-transform: capitalize;
background: inherit;
background-clip: text;
-webkit-background-clip: text;
}
.override {
gap: 8px;
display: flex;
.picker {
width: 38px;
height: 38px;
display: grid;
cursor: pointer;
place-items: center;
border-radius: var(--border-radius);
background: var(--primary-background);
}
input[type="text"] {
width: 0;
min-width: 0;
flex-grow: 1;
}
}
.input {
width: 0;
height: 0;
position: relative;
input {
opacity: 0;
border: none;
display: block;
cursor: pointer;
position: relative;
top: 48px;
}
}
}
}
}
.sessions {