mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-25 16:40:58 -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 { getRenderer } from "../../../../lib/renderer/Singleton";
|
||||
|
||||
const Bar = styled.div`
|
||||
export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>`
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
${(props) =>
|
||||
props.position === "bottom" &&
|
||||
css`
|
||||
top: -26px;
|
||||
`}
|
||||
|
||||
height: 28px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
@ -24,10 +29,29 @@ const Bar = styled.div`
|
|||
padding: 0 8px;
|
||||
user-select: none;
|
||||
justify-content: space-between;
|
||||
color: var(--secondary-foreground);
|
||||
transition: color ease-in-out 0.08s;
|
||||
|
||||
${(props) =>
|
||||
props.accent
|
||||
? css`
|
||||
color: var(--accent-contrast);
|
||||
background: var(--accent);
|
||||
`
|
||||
: css`
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
`}
|
||||
|
||||
${(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 {
|
||||
display: flex;
|
||||
|
@ -58,7 +82,7 @@ export default observer(({ channel }: { channel: Channel }) => {
|
|||
if (renderer.state !== "RENDER" || renderer.atBottom) return null;
|
||||
|
||||
return (
|
||||
<Bar>
|
||||
<Bar position="bottom">
|
||||
<div onClick={() => renderer.jumpToBottom(true)}>
|
||||
<div>
|
||||
<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;
|
||||
text-transform: uppercase;
|
||||
|
||||
color: var(--foreground);
|
||||
color: var(--accent-contrast);
|
||||
background: var(--accent);
|
||||
border-radius: calc(var(--border-radius) / 2);
|
||||
`;
|
||||
|
|
|
@ -134,8 +134,8 @@ export default observer(() => {
|
|||
</div>
|
||||
<span
|
||||
style={{
|
||||
color: getContrastingColour(
|
||||
theme.getVariable(key),
|
||||
color: theme.getContrastingVariable(
|
||||
key,
|
||||
theme.getVariable("primary-background"),
|
||||
),
|
||||
}}>
|
||||
|
@ -168,14 +168,3 @@ export default observer(() => {
|
|||
</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,21 +102,20 @@ interface PageHeaderProps {
|
|||
export const PageHeader = observer(
|
||||
({ children, icon, noBurger }: PageHeaderProps) => {
|
||||
const layout = useApplicationState().layout;
|
||||
const visible = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||
|
||||
return (
|
||||
<Header placement="primary">
|
||||
<Header placement="primary" borders={!visible}>
|
||||
{!noBurger && <HamburgerAction />}
|
||||
<IconContainer
|
||||
onClick={() =>
|
||||
layout.toggleSectionState(SIDEBAR_CHANNELS, true)
|
||||
}>
|
||||
{!isTouchscreenDevice &&
|
||||
layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
||||
{!isTouchscreenDevice && visible && (
|
||||
<ChevronLeft size={18} />
|
||||
)}
|
||||
{icon}
|
||||
{!isTouchscreenDevice &&
|
||||
!layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
||||
{!isTouchscreenDevice && !visible && (
|
||||
<ChevronRight size={18} />
|
||||
)}
|
||||
</IconContainer>
|
||||
|
|
|
@ -279,7 +279,6 @@ export const PRESETS: Record<string, Theme> = {
|
|||
},
|
||||
};
|
||||
|
||||
const keys = Object.keys(PRESETS.dark);
|
||||
const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
||||
:root {
|
||||
${(props) => generateVariables(props.theme)}
|
||||
|
@ -288,7 +287,6 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
|||
|
||||
export const generateVariables = (theme: Theme) => {
|
||||
return (Object.keys(theme) as Variables[]).map((key) => {
|
||||
if (!keys.includes(key)) return;
|
||||
return `--${key}: ${theme[key]};`;
|
||||
});
|
||||
};
|
||||
|
@ -326,7 +324,7 @@ export default observer(() => {
|
|||
return () => window.removeEventListener("resize", resize);
|
||||
}, [root]);
|
||||
|
||||
const variables = theme.getVariables();
|
||||
const variables = theme.computeVariables();
|
||||
return (
|
||||
<>
|
||||
<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) {
|
||||
this.settings.set("appearance:theme:overrides", {
|
||||
...this.settings.get("appearance:theme:overrides"),
|
||||
|
@ -113,6 +129,15 @@ export default class STheme {
|
|||
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) {
|
||||
this.settings.set("appearance:theme:font", font);
|
||||
}
|
||||
|
@ -175,3 +200,17 @@ export default class STheme {
|
|||
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 { Text } from "preact-i18n";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import { useEffect, useMemo } from "preact/hooks";
|
||||
|
||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||
|
||||
|
@ -19,6 +19,7 @@ import { useClient } from "../../context/revoltjs/RevoltClient";
|
|||
import AgeGate from "../../components/common/AgeGate";
|
||||
import MessageBox from "../../components/common/messaging/MessageBox";
|
||||
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 Header, { PageHeader } from "../../components/ui/Header";
|
||||
|
||||
|
@ -87,6 +88,15 @@ export function Channel({ id }: { id: string }) {
|
|||
const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||
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.
|
||||
useEffect(() => {
|
||||
const checkUnread = () =>
|
||||
|
@ -119,6 +129,7 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
|||
<ChannelMain>
|
||||
<ChannelContent>
|
||||
<VoiceHeader id={channel._id} />
|
||||
<NewMessages channel={channel} last_id={last_id} />
|
||||
<MessageArea channel={channel} />
|
||||
<TypingIndicator channel={channel} />
|
||||
<JumpToBottom channel={channel} />
|
||||
|
|
|
@ -56,7 +56,7 @@ const BotBadge = styled.div`
|
|||
margin-inline-start: 2px;
|
||||
text-transform: uppercase;
|
||||
|
||||
color: var(--foreground);
|
||||
color: var(--accent-contrast);
|
||||
background: var(--accent);
|
||||
border-radius: calc(var(--border-radius) / 2);
|
||||
`;
|
||||
|
|
Loading…
Reference in a new issue