mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-29 10:20:59 -05:00
chore: clean up contrasting colours code
This commit is contained in:
parent
a46fbcf409
commit
fee56d8f54
9 changed files with 141 additions and 34 deletions
|
@ -8,12 +8,17 @@ import { Text } from "preact-i18n";
|
||||||
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
||||||
import { getRenderer } from "../../../../lib/renderer/Singleton";
|
import { getRenderer } from "../../../../lib/renderer/Singleton";
|
||||||
|
|
||||||
const Bar = styled.div`
|
export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>`
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
top: -26px;
|
${(props) =>
|
||||||
|
props.position === "bottom" &&
|
||||||
|
css`
|
||||||
|
top: -26px;
|
||||||
|
`}
|
||||||
|
|
||||||
height: 28px;
|
height: 28px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -24,10 +29,29 @@ const Bar = styled.div`
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
color: var(--secondary-foreground);
|
|
||||||
transition: color ease-in-out 0.08s;
|
transition: color ease-in-out 0.08s;
|
||||||
background: var(--secondary-background);
|
|
||||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
${(props) =>
|
||||||
|
props.accent
|
||||||
|
? css`
|
||||||
|
color: var(--accent-contrast);
|
||||||
|
background: var(--accent);
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
background: var(--secondary-background);
|
||||||
|
`}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.position === "top"
|
||||||
|
? css`
|
||||||
|
border-radius: 0 0 var(--border-radius)
|
||||||
|
var(--border-radius);
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
border-radius: var(--border-radius) var(--border-radius) 0
|
||||||
|
0;
|
||||||
|
`}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -58,7 +82,7 @@ export default observer(({ channel }: { channel: Channel }) => {
|
||||||
if (renderer.state !== "RENDER" || renderer.atBottom) return null;
|
if (renderer.state !== "RENDER" || renderer.atBottom) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Bar>
|
<Bar position="bottom">
|
||||||
<div onClick={() => renderer.jumpToBottom(true)}>
|
<div onClick={() => renderer.jumpToBottom(true)}>
|
||||||
<div>
|
<div>
|
||||||
<Text id="app.main.channel.misc.viewing_old" />
|
<Text id="app.main.channel.misc.viewing_old" />
|
||||||
|
|
47
src/components/common/messaging/bars/NewMessages.tsx
Normal file
47
src/components/common/messaging/bars/NewMessages.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { UpArrowAlt } from "@styled-icons/boxicons-regular";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
|
import { decodeTime } from "ulid";
|
||||||
|
|
||||||
|
import { getRenderer } from "../../../../lib/renderer/Singleton";
|
||||||
|
|
||||||
|
import { dayjs } from "../../../../context/Locale";
|
||||||
|
|
||||||
|
import { Bar } from "./JumpToBottom";
|
||||||
|
|
||||||
|
export default observer(
|
||||||
|
({ channel, last_id }: { channel: Channel; last_id?: string }) => {
|
||||||
|
const renderer = getRenderer(channel);
|
||||||
|
const history = useHistory();
|
||||||
|
if (renderer.state !== "RENDER") return null;
|
||||||
|
if (!last_id) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Bar position="top" accent>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (channel.channel_type === "TextChannel") {
|
||||||
|
history.push(
|
||||||
|
`/server/${channel.server_id}/channel/${channel._id}/${last_id}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
history.push(
|
||||||
|
`/channel/${channel._id}/${last_id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
New messages since{" "}
|
||||||
|
{dayjs(decodeTime(last_id)).fromNow()}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Click to jump to start. <UpArrowAlt size={20} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Bar>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
|
@ -24,7 +24,7 @@ const BotBadge = styled.div`
|
||||||
margin-inline-start: 2px;
|
margin-inline-start: 2px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--accent-contrast);
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
border-radius: calc(var(--border-radius) / 2);
|
border-radius: calc(var(--border-radius) / 2);
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -134,8 +134,8 @@ export default observer(() => {
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
color: getContrastingColour(
|
color: theme.getContrastingVariable(
|
||||||
theme.getVariable(key),
|
key,
|
||||||
theme.getVariable("primary-background"),
|
theme.getVariable("primary-background"),
|
||||||
),
|
),
|
||||||
}}>
|
}}>
|
||||||
|
@ -168,14 +168,3 @@ export default observer(() => {
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
|
@ -102,23 +102,22 @@ interface PageHeaderProps {
|
||||||
export const PageHeader = observer(
|
export const PageHeader = observer(
|
||||||
({ children, icon, noBurger }: PageHeaderProps) => {
|
({ children, icon, noBurger }: PageHeaderProps) => {
|
||||||
const layout = useApplicationState().layout;
|
const layout = useApplicationState().layout;
|
||||||
|
const visible = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header placement="primary">
|
<Header placement="primary" borders={!visible}>
|
||||||
{!noBurger && <HamburgerAction />}
|
{!noBurger && <HamburgerAction />}
|
||||||
<IconContainer
|
<IconContainer
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
layout.toggleSectionState(SIDEBAR_CHANNELS, true)
|
layout.toggleSectionState(SIDEBAR_CHANNELS, true)
|
||||||
}>
|
}>
|
||||||
{!isTouchscreenDevice &&
|
{!isTouchscreenDevice && visible && (
|
||||||
layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
<ChevronLeft size={18} />
|
||||||
<ChevronLeft size={18} />
|
)}
|
||||||
)}
|
|
||||||
{icon}
|
{icon}
|
||||||
{!isTouchscreenDevice &&
|
{!isTouchscreenDevice && !visible && (
|
||||||
!layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
<ChevronRight size={18} />
|
||||||
<ChevronRight size={18} />
|
)}
|
||||||
)}
|
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
{children}
|
{children}
|
||||||
</Header>
|
</Header>
|
||||||
|
|
|
@ -279,7 +279,6 @@ export const PRESETS: Record<string, Theme> = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const keys = Object.keys(PRESETS.dark);
|
|
||||||
const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
||||||
:root {
|
:root {
|
||||||
${(props) => generateVariables(props.theme)}
|
${(props) => generateVariables(props.theme)}
|
||||||
|
@ -288,7 +287,6 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
||||||
|
|
||||||
export const generateVariables = (theme: Theme) => {
|
export const generateVariables = (theme: Theme) => {
|
||||||
return (Object.keys(theme) as Variables[]).map((key) => {
|
return (Object.keys(theme) as Variables[]).map((key) => {
|
||||||
if (!keys.includes(key)) return;
|
|
||||||
return `--${key}: ${theme[key]};`;
|
return `--${key}: ${theme[key]};`;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -326,7 +324,7 @@ export default observer(() => {
|
||||||
return () => window.removeEventListener("resize", resize);
|
return () => window.removeEventListener("resize", resize);
|
||||||
}, [root]);
|
}, [root]);
|
||||||
|
|
||||||
const variables = theme.getVariables();
|
const variables = theme.computeVariables();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
|
|
|
@ -96,6 +96,22 @@ export default class STheme {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed computeVariables(): Theme {
|
||||||
|
const variables = this.getVariables() as Record<
|
||||||
|
string,
|
||||||
|
string | boolean
|
||||||
|
>;
|
||||||
|
|
||||||
|
for (const key of Object.keys(variables)) {
|
||||||
|
const value = variables[key];
|
||||||
|
if (typeof value === "string") {
|
||||||
|
variables[key + "-contrast"] = getContrastingColour(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables as unknown as Theme;
|
||||||
|
}
|
||||||
|
|
||||||
@action setVariable(key: Variables, value: string) {
|
@action setVariable(key: Variables, value: string) {
|
||||||
this.settings.set("appearance:theme:overrides", {
|
this.settings.set("appearance:theme:overrides", {
|
||||||
...this.settings.get("appearance:theme:overrides"),
|
...this.settings.get("appearance:theme:overrides"),
|
||||||
|
@ -113,6 +129,15 @@ export default class STheme {
|
||||||
PRESETS[this.getBase()]?.[key])!;
|
PRESETS[this.getBase()]?.[key])!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the contrasting colour of a variable by its key.
|
||||||
|
* @param key Variable
|
||||||
|
* @returns Contrasting value
|
||||||
|
*/
|
||||||
|
@computed getContrastingVariable(key: Variables, fallback?: string) {
|
||||||
|
return getContrastingColour(this.getVariable(key), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
@action setFont(font: Fonts) {
|
@action setFont(font: Fonts) {
|
||||||
this.settings.set("appearance:theme:font", font);
|
this.settings.set("appearance:theme:font", font);
|
||||||
}
|
}
|
||||||
|
@ -175,3 +200,17 @@ export default class STheme {
|
||||||
this.settings.remove("appearance:theme:css");
|
this.settings.remove("appearance:theme:css");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getContrastingColour(hex: string, fallback = "black"): string {
|
||||||
|
// TODO: Switch to color-parse
|
||||||
|
// Try parse hex value.
|
||||||
|
hex = hex.replace("#", "");
|
||||||
|
const r = parseInt(hex.substr(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(hex.substr(2, 2), 16) / 255;
|
||||||
|
const b = parseInt(hex.substr(4, 2), 16) / 255;
|
||||||
|
|
||||||
|
if (isNaN(r) || isNaN(g) || isNaN(b))
|
||||||
|
return fallback ? getContrastingColour(fallback) : "black";
|
||||||
|
|
||||||
|
return r * 0.299 + g * 0.587 + b * 0.114 >= 0.186 ? "black" : "white";
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useEffect } from "preact/hooks";
|
import { useEffect, useMemo } from "preact/hooks";
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
import AgeGate from "../../components/common/AgeGate";
|
import AgeGate from "../../components/common/AgeGate";
|
||||||
import MessageBox from "../../components/common/messaging/MessageBox";
|
import MessageBox from "../../components/common/messaging/MessageBox";
|
||||||
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
||||||
|
import NewMessages from "../../components/common/messaging/bars/NewMessages";
|
||||||
import TypingIndicator from "../../components/common/messaging/bars/TypingIndicator";
|
import TypingIndicator from "../../components/common/messaging/bars/TypingIndicator";
|
||||||
import Header, { PageHeader } from "../../components/ui/Header";
|
import Header, { PageHeader } from "../../components/ui/Header";
|
||||||
|
|
||||||
|
@ -87,6 +88,15 @@ export function Channel({ id }: { id: string }) {
|
||||||
const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||||
const layout = useApplicationState().layout;
|
const layout = useApplicationState().layout;
|
||||||
|
|
||||||
|
// Cache the unread location.
|
||||||
|
const last_id = useMemo(
|
||||||
|
() =>
|
||||||
|
(channel.unread
|
||||||
|
? channel.client.unreads?.getUnread(channel._id)?.last_id
|
||||||
|
: undefined) ?? undefined,
|
||||||
|
[channel],
|
||||||
|
);
|
||||||
|
|
||||||
// Mark channel as read.
|
// Mark channel as read.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkUnread = () =>
|
const checkUnread = () =>
|
||||||
|
@ -119,6 +129,7 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||||
<ChannelMain>
|
<ChannelMain>
|
||||||
<ChannelContent>
|
<ChannelContent>
|
||||||
<VoiceHeader id={channel._id} />
|
<VoiceHeader id={channel._id} />
|
||||||
|
<NewMessages channel={channel} last_id={last_id} />
|
||||||
<MessageArea channel={channel} />
|
<MessageArea channel={channel} />
|
||||||
<TypingIndicator channel={channel} />
|
<TypingIndicator channel={channel} />
|
||||||
<JumpToBottom channel={channel} />
|
<JumpToBottom channel={channel} />
|
||||||
|
|
|
@ -56,7 +56,7 @@ const BotBadge = styled.div`
|
||||||
margin-inline-start: 2px;
|
margin-inline-start: 2px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
color: var(--foreground);
|
color: var(--accent-contrast);
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
border-radius: calc(var(--border-radius) / 2);
|
border-radius: calc(var(--border-radius) / 2);
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in a new issue