mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-22 07:00:58 -05:00
chore: merge branch 'ui/glass-header'
This commit is contained in:
commit
c26ef4c2b2
45 changed files with 863 additions and 396 deletions
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 35894f6b1ecadc9df4ab77a5de9c9d5256a36582
|
Subproject commit 1d3e85e7f6d0ad7a590854e240d7c47291f3e2cf
|
|
@ -59,6 +59,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"color-rgba": "^2.3.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"klaw": "^3.0.0",
|
"klaw": "^3.0.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
|
@ -95,6 +96,7 @@
|
||||||
"@traptitech/markdown-it-katex": "^3.4.3",
|
"@traptitech/markdown-it-katex": "^3.4.3",
|
||||||
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
"@traptitech/markdown-it-spoiler": "^1.1.6",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^2.0.2",
|
||||||
|
"@types/color-rgba": "^2.1.0",
|
||||||
"@types/lodash.defaultsdeep": "^4.6.6",
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
||||||
"@types/lodash.isequal": "^4.5.5",
|
"@types/lodash.isequal": "^4.5.5",
|
||||||
"@types/markdown-it": "^12.0.2",
|
"@types/markdown-it": "^12.0.2",
|
||||||
|
|
|
@ -673,6 +673,7 @@ export const emojiDictionary = {
|
||||||
mandarin: "🍊",
|
mandarin: "🍊",
|
||||||
lemon: "🍋",
|
lemon: "🍋",
|
||||||
banana: "🍌",
|
banana: "🍌",
|
||||||
|
nanner: "🍌",
|
||||||
pineapple: "🍍",
|
pineapple: "🍍",
|
||||||
mango: "🥭",
|
mango: "🥭",
|
||||||
apple: "🍎",
|
apple: "🍎",
|
||||||
|
@ -876,6 +877,7 @@ export const emojiDictionary = {
|
||||||
train: "🚋",
|
train: "🚋",
|
||||||
bus: "🚌",
|
bus: "🚌",
|
||||||
oncoming_bus: "🚍",
|
oncoming_bus: "🚍",
|
||||||
|
trolley: "🚎",
|
||||||
trolleybus: "🚎",
|
trolleybus: "🚎",
|
||||||
minibus: "🚐",
|
minibus: "🚐",
|
||||||
ambulance: "🚑",
|
ambulance: "🚑",
|
||||||
|
|
|
@ -4,37 +4,84 @@ import { observer } from "mobx-react-lite";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { ServerPermission } from "revolt.js/dist/api/permissions";
|
import { ServerPermission } from "revolt.js/dist/api/permissions";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "revolt.js/dist/maps/Servers";
|
||||||
import styled from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
import Header from "../ui/Header";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import IconButton from "../ui/IconButton";
|
import IconButton from "../ui/IconButton";
|
||||||
|
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
server: Server;
|
server: Server;
|
||||||
|
background?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerName = styled.div`
|
const ServerBanner = styled.div<Omit<Props, "server">>`
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: norepeat;
|
||||||
|
background-position: center center;
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.background
|
||||||
|
? css`
|
||||||
|
height: 120px;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
var(--secondary-background),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
background-color: var(--secondary-header);
|
||||||
|
`}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
height: var(--header-height);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default observer(({ server }: Props) => {
|
export default observer(({ server }: Props) => {
|
||||||
const bannerURL = server.generateBannerURL({ width: 480 });
|
const bannerURL = server.generateBannerURL({ width: 480 });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header
|
<ServerBanner
|
||||||
borders
|
|
||||||
placement="secondary"
|
|
||||||
background={typeof bannerURL !== "undefined"}
|
background={typeof bannerURL !== "undefined"}
|
||||||
style={{
|
style={{
|
||||||
background: bannerURL ? `url('${bannerURL}')` : undefined,
|
backgroundImage: bannerURL ? `url('${bannerURL}')` : undefined,
|
||||||
}}>
|
}}>
|
||||||
|
<div className="container">
|
||||||
{server.flags && server.flags & 1 ? (
|
{server.flags && server.flags & 1 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={<Text id="app.special.server-badges.official" />}
|
content={
|
||||||
|
<Text id="app.special.server-badges.official" />
|
||||||
|
}
|
||||||
placement={"bottom-start"}>
|
placement={"bottom-start"}>
|
||||||
<svg width="20" height="20">
|
<svg width="20" height="20">
|
||||||
<image
|
<image
|
||||||
|
@ -57,7 +104,9 @@ export default observer(({ server }: Props) => {
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{server.flags && server.flags & 2 ? (
|
{server.flags && server.flags & 2 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={<Text id="app.special.server-badges.verified" />}
|
content={
|
||||||
|
<Text id="app.special.server-badges.verified" />
|
||||||
|
}
|
||||||
placement={"bottom-start"}>
|
placement={"bottom-start"}>
|
||||||
<svg width="20" height="20">
|
<svg width="20" height="20">
|
||||||
<image
|
<image
|
||||||
|
@ -66,22 +115,24 @@ export default observer(({ server }: Props) => {
|
||||||
width="20"
|
width="20"
|
||||||
/>
|
/>
|
||||||
<foreignObject x="2" y="2" width="15" height="15">
|
<foreignObject x="2" y="2" width="15" height="15">
|
||||||
<Check size={15} color="black" strokeWidth={8} />
|
<Check
|
||||||
|
size={15}
|
||||||
|
color="black"
|
||||||
|
strokeWidth={8}
|
||||||
|
/>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
</svg>
|
</svg>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
<div className="title">{server.name}</div>
|
||||||
<ServerName>{server.name}</ServerName>
|
|
||||||
{(server.permission & ServerPermission.ManageServer) > 0 && (
|
{(server.permission & ServerPermission.ManageServer) > 0 && (
|
||||||
<div className="actions">
|
|
||||||
<Link to={`/server/${server._id}/settings`}>
|
<Link to={`/server/${server._id}/settings`}>
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<Cog size={24} />
|
<Cog size={20} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</Header>
|
</div>
|
||||||
|
</ServerBanner>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -80,7 +80,7 @@ const Blocked = styled.div`
|
||||||
color: var(--tertiary-foreground);
|
color: var(--tertiary-foreground);
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
padding: 14px 14px 14px 0;
|
padding: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
@ -89,13 +89,17 @@ const Blocked = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Action = styled.div`
|
const Action = styled.div`
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 48px;
|
width: 34px;
|
||||||
padding: 12px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
/*padding: 14px 0 14px 14px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
justify-content: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
${() =>
|
${() =>
|
||||||
|
|
|
@ -13,19 +13,25 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>`
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
> div {
|
${(props) =>
|
||||||
|
props.position === "top" &&
|
||||||
|
css`
|
||||||
|
top: 0;
|
||||||
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.position === "bottom" &&
|
props.position === "bottom" &&
|
||||||
css`
|
css`
|
||||||
top: -26px;
|
top: 65px;
|
||||||
|
|
||||||
${() =>
|
${() =>
|
||||||
isTouchscreenDevice &&
|
isTouchscreenDevice &&
|
||||||
css`
|
css`
|
||||||
top: -32px;
|
top: -90px;
|
||||||
`}
|
`}
|
||||||
`}
|
`}
|
||||||
|
|
||||||
|
> div {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -52,6 +58,7 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>`
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.position === "top"
|
props.position === "top"
|
||||||
? css`
|
? css`
|
||||||
|
top: 48px;
|
||||||
border-radius: 0 0 var(--border-radius)
|
border-radius: 0 0 var(--border-radius)
|
||||||
var(--border-radius);
|
var(--border-radius);
|
||||||
`
|
`
|
||||||
|
@ -60,6 +67,12 @@ export const Bar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>`
|
||||||
0;
|
0;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
|
${() =>
|
||||||
|
isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
top: 56px;
|
||||||
|
`}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -49,8 +49,12 @@ export default observer(
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div>
|
||||||
New messages since{" "}
|
<Text
|
||||||
{dayjs(decodeTime(last_id)).fromNow()}
|
id="app.main.channel.misc.new_messages"
|
||||||
|
fields={{
|
||||||
|
time_ago: dayjs(decodeTime(last_id)).fromNow(),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Text id="app.main.channel.misc.jump_beginning" />
|
<Text id="app.main.channel.misc.jump_beginning" />
|
||||||
|
|
|
@ -25,7 +25,11 @@ const Base = styled.div`
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: calc(100% - var(--scrollbar-thickness));
|
width: calc(100% - var(--scrollbar-thickness));
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
background: var(--secondary-background);
|
background-color: rgba(
|
||||||
|
var(--secondary-background-rgb),
|
||||||
|
max(var(--min-opacity), 0.75)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatars {
|
.avatars {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Group } from "@styled-icons/boxicons-solid";
|
||||||
import { autorun } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
@ -47,7 +48,7 @@ const EmbedInviteBase = styled.div`
|
||||||
|
|
||||||
const EmbedInviteDetails = styled.div`
|
const EmbedInviteDetails = styled.div`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding-left: 12px;
|
padding-inline-start: 12px;
|
||||||
${() =>
|
${() =>
|
||||||
isTouchscreenDevice &&
|
isTouchscreenDevice &&
|
||||||
css`
|
css`
|
||||||
|
@ -63,7 +64,14 @@ const EmbedInviteName = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const EmbedInviteMemberCount = styled.div`
|
const EmbedInviteMemberCount = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -119,6 +127,7 @@ export function EmbedInvite({ code }: Props) {
|
||||||
<EmbedInviteDetails>
|
<EmbedInviteDetails>
|
||||||
<EmbedInviteName>{invite.server_name}</EmbedInviteName>
|
<EmbedInviteName>{invite.server_name}</EmbedInviteName>
|
||||||
<EmbedInviteMemberCount>
|
<EmbedInviteMemberCount>
|
||||||
|
<Group size={12} />
|
||||||
{invite.member_count.toLocaleString()}{" "}
|
{invite.member_count.toLocaleString()}{" "}
|
||||||
{invite.member_count === 1 ? "member" : "members"}
|
{invite.member_count === 1 ? "member" : "members"}
|
||||||
</EmbedInviteMemberCount>
|
</EmbedInviteMemberCount>
|
||||||
|
|
|
@ -17,10 +17,10 @@ const Base = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Navbar = styled.div`
|
const Navbar = styled.div`
|
||||||
z-index: 100;
|
z-index: 500;
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 500px;
|
||||||
height: var(--bottom-navigation-height);
|
height: var(--bottom-navigation-height);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -71,7 +71,12 @@ export default observer(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
history.push(layout.getLastHomePath());
|
const path = layout.getLastHomePath();
|
||||||
|
if (path === "/friends") {
|
||||||
|
history.push("/");
|
||||||
|
} else {
|
||||||
|
history.push(path);
|
||||||
|
}
|
||||||
}}>
|
}}>
|
||||||
<Message size={24} />
|
<Message size={24} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
|
@ -8,6 +8,13 @@ export default styled.div`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
/*background: var(--background);*/
|
||||||
|
|
||||||
|
background-color: rgba(
|
||||||
|
var(--background-rgb),
|
||||||
|
max(var(--min-opacity), 0.75)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GenericSidebarBase = styled.div<{
|
export const GenericSidebarBase = styled.div<{
|
||||||
|
@ -21,10 +28,15 @@ export const GenericSidebarBase = styled.div<{
|
||||||
/*border-end-start-radius: 8px;*/
|
/*border-end-start-radius: 8px;*/
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
|
|
||||||
> :nth-child(1) {
|
/*> :nth-child(1) {
|
||||||
border-end-start-radius: 8px;
|
//border-end-start-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> :nth-child(2) {
|
||||||
|
margin-top: 48px;
|
||||||
|
background: red;
|
||||||
|
}*/
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.mobilePadding &&
|
props.mobilePadding &&
|
||||||
isTouchscreenDevice &&
|
isTouchscreenDevice &&
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { X, Crown } from "@styled-icons/boxicons-regular";
|
import { X } from "@styled-icons/boxicons-regular";
|
||||||
|
import { Crown } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Presence } from "revolt-api/types/Users";
|
import { Presence } from "revolt-api/types/Users";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Link, useLocation, useParams } from "react-router-dom";
|
import { Link, useLocation, useParams } from "react-router-dom";
|
||||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||||
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useEffect } from "preact/hooks";
|
import { useContext, useEffect } from "preact/hooks";
|
||||||
|
@ -27,6 +28,21 @@ import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
|
||||||
import ButtonItem, { ChannelButton } from "../items/ButtonItem";
|
import ButtonItem, { ChannelButton } from "../items/ButtonItem";
|
||||||
import ConnectionStatus from "../items/ConnectionStatus";
|
import ConnectionStatus from "../items/ConnectionStatus";
|
||||||
|
|
||||||
|
const Navbar = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
${() =>
|
||||||
|
isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
height: 56px;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
|
@ -55,6 +71,9 @@ export default observer(() => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericSidebarBase mobilePadding>
|
<GenericSidebarBase mobilePadding>
|
||||||
|
<Navbar>
|
||||||
|
<Text id="app.home.directs" />
|
||||||
|
</Navbar>
|
||||||
<ConnectionStatus />
|
<ConnectionStatus />
|
||||||
<GenericSidebarList>
|
<GenericSidebarList>
|
||||||
<ConditionalLink active={pathname === "/"} to="/">
|
<ConditionalLink active={pathname === "/"} to="/">
|
||||||
|
|
|
@ -95,6 +95,7 @@ const ServerList = styled.div`
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-top: -2px;
|
||||||
|
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
@ -168,6 +169,7 @@ const ServerCircle = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
.circle {
|
.circle {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -384,7 +386,7 @@ export default observer(() => {
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ServerCircle>
|
</ServerCircle>
|
||||||
<ServerCircle>
|
{/*<ServerCircle>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
<div
|
<div
|
||||||
|
@ -394,14 +396,13 @@ export default observer(() => {
|
||||||
gap: "4px",
|
gap: "4px",
|
||||||
}}>
|
}}>
|
||||||
<div>Discover Public Servers</div>
|
<div>Discover Public Servers</div>
|
||||||
<LinkExternal size={12} />
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
placement="right">
|
placement="right">
|
||||||
<div className="circle">
|
<div className="circle">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<a
|
<a
|
||||||
href="https://revolt.social"
|
href="#"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<Compass size={32} />
|
<Compass size={32} />
|
||||||
|
@ -409,7 +410,7 @@ export default observer(() => {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ServerCircle>
|
</ServerCircle>*/}
|
||||||
</ServerList>
|
</ServerList>
|
||||||
<PaintCounter small />
|
<PaintCounter small />
|
||||||
{!isTouchscreenDevice && (
|
{!isTouchscreenDevice && (
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Redirect, useParams } from "react-router";
|
import { Redirect, useParams } from "react-router";
|
||||||
|
import { Server } from "revolt.js/dist/maps/Servers";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { attachContextMenu } from "preact-context-menu";
|
import { attachContextMenu } from "preact-context-menu";
|
||||||
|
@ -48,6 +49,10 @@ const ServerList = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
server: Server;
|
||||||
|
}
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const state = useApplicationState();
|
const state = useApplicationState();
|
||||||
|
|
|
@ -6,9 +6,12 @@ import { Presence } from "revolt-api/types/Users";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
import { Server } from "revolt.js/dist/maps/Servers";
|
import { Server } from "revolt.js/dist/maps/Servers";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { useContext, useEffect, useMemo } from "preact/hooks";
|
import { useContext, useEffect, useMemo } from "preact/hooks";
|
||||||
|
|
||||||
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ClientStatus,
|
ClientStatus,
|
||||||
StatusContext,
|
StatusContext,
|
||||||
|
@ -18,6 +21,15 @@ import {
|
||||||
import { GenericSidebarBase } from "../SidebarBase";
|
import { GenericSidebarBase } from "../SidebarBase";
|
||||||
import MemberList, { MemberListGroup } from "./MemberList";
|
import MemberList, { MemberListGroup } from "./MemberList";
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
padding-top: 48px;
|
||||||
|
|
||||||
|
${isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
padding-top: 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
export default function MemberSidebar() {
|
export default function MemberSidebar() {
|
||||||
const channel = useClient().channels.get(
|
const channel = useClient().channels.get(
|
||||||
useParams<{ channel: string }>().channel,
|
useParams<{ channel: string }>().channel,
|
||||||
|
@ -157,6 +169,10 @@ export const GroupMemberSidebar = observer(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericSidebarBase>
|
<GenericSidebarBase>
|
||||||
|
<Container>
|
||||||
|
{/*{isTouchscreenDevice && <div>Group settings go here</div>}*/}
|
||||||
|
</Container>
|
||||||
|
|
||||||
<MemberList entries={entries} context={channel} />
|
<MemberList entries={entries} context={channel} />
|
||||||
</GenericSidebarBase>
|
</GenericSidebarBase>
|
||||||
);
|
);
|
||||||
|
@ -180,6 +196,9 @@ export const ServerMemberSidebar = observer(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericSidebarBase>
|
<GenericSidebarBase>
|
||||||
|
<Container>
|
||||||
|
{/*{isTouchscreenDevice && <div>Server settings go here</div>}*/}
|
||||||
|
</Container>
|
||||||
<MemberList entries={entries} context={channel} />
|
<MemberList entries={entries} context={channel} />
|
||||||
</GenericSidebarBase>
|
</GenericSidebarBase>
|
||||||
);
|
);
|
||||||
|
|
|
@ -218,19 +218,6 @@ export const DisplaySeasonalShim = observer(() => {
|
||||||
const settings = useApplicationState().settings;
|
const settings = useApplicationState().settings;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<h3>
|
|
||||||
<Text id="app.settings.pages.appearance.theme_options.title" />
|
|
||||||
</h3>
|
|
||||||
{/* TOFIX: WIP feature - follows system theme */}
|
|
||||||
{/*<Checkbox
|
|
||||||
checked={settings.get("appearance:seasonal") ?? true}
|
|
||||||
onChange={(v) => settings.set("appearance:seasonal", v)}
|
|
||||||
description={
|
|
||||||
<Text id="app.settings.pages.appearance.theme_options.follow_desc" />
|
|
||||||
}>
|
|
||||||
<Text id="app.settings.pages.appearance.theme_options.follow" />
|
|
||||||
</Checkbox>*/}
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={settings.get("appearance:seasonal") ?? true}
|
checked={settings.get("appearance:seasonal") ?? true}
|
||||||
onChange={(v) => settings.set("appearance:seasonal", v)}
|
onChange={(v) => settings.set("appearance:seasonal", v)}
|
||||||
|
@ -239,7 +226,24 @@ export const DisplaySeasonalShim = observer(() => {
|
||||||
}>
|
}>
|
||||||
<Text id="app.settings.pages.appearance.theme_options.seasonal" />
|
<Text id="app.settings.pages.appearance.theme_options.seasonal" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</>
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component providing a way to toggle transparency effects.
|
||||||
|
*/
|
||||||
|
export const DisplayTransparencyShim = observer(() => {
|
||||||
|
const settings = useApplicationState().settings;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={settings.get("appearance:transparency") ?? true}
|
||||||
|
onChange={(v) => settings.set("appearance:transparency", v)}
|
||||||
|
description={
|
||||||
|
<Text id="app.settings.pages.appearance.theme_options.transparency_desc" />
|
||||||
|
}>
|
||||||
|
<Text id="app.settings.pages.appearance.theme_options.transparency" />
|
||||||
|
</Checkbox>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -19,4 +19,8 @@ export default styled.select`
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 0 1.5pt var(--accent);
|
box-shadow: 0 0 0 1.5pt var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
export default styled.details<{ sticky?: boolean; large?: boolean }>`
|
export default styled.details<{ sticky?: boolean; large?: boolean }>`
|
||||||
summary {
|
summary {
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.sticky &&
|
props.sticky &&
|
||||||
css`
|
css`
|
||||||
top: -1px;
|
top: 48px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
${() =>
|
||||||
|
isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
top: 56px;
|
||||||
|
`}
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Menu,
|
Menu,
|
||||||
} from "@styled-icons/boxicons-regular";
|
} from "@styled-icons/boxicons-regular";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
@ -14,14 +15,16 @@ import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
|
||||||
import { Children } from "../../types/Preact";
|
import { Children } from "../../types/Preact";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
borders?: boolean;
|
topBorder?: boolean;
|
||||||
|
bottomBorder?: boolean;
|
||||||
|
|
||||||
background?: boolean;
|
background?: boolean;
|
||||||
|
transparent?: boolean;
|
||||||
placement: "primary" | "secondary";
|
placement: "primary" | "secondary";
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header = styled.div<Props>`
|
const Header = styled.div<Props>`
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 48px;
|
|
||||||
flex: 0 auto;
|
flex: 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -29,15 +32,11 @@ const Header = styled.div<Props>`
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
height: var(--header-height);
|
||||||
|
|
||||||
background-size: cover !important;
|
background-size: cover !important;
|
||||||
background-position: center !important;
|
background-position: center !important;
|
||||||
background-color: var(--primary-header);
|
|
||||||
|
|
||||||
/*> div {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -48,10 +47,20 @@ const Header = styled.div<Props>`
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
${() =>
|
${(props) =>
|
||||||
isTouchscreenDevice &&
|
props.transparent
|
||||||
css`
|
? css`
|
||||||
height: 56px;
|
background-color: rgba(
|
||||||
|
var(--primary-header-rgb),
|
||||||
|
max(var(--min-opacity), 0.75)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
z-index: 20;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
background-color: var(--primary-header);
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
|
@ -71,10 +80,16 @@ const Header = styled.div<Props>`
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.borders &&
|
props.topBorder &&
|
||||||
css`
|
css`
|
||||||
border-start-start-radius: 8px;
|
border-start-start-radius: 8px;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.bottomBorder &&
|
||||||
|
css`
|
||||||
|
border-end-start-radius: 8px;
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
||||||
|
@ -98,19 +113,24 @@ const IconContainer = styled.div`
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface PageHeaderProps {
|
type PageHeaderProps = Omit<Props, "placement" | "borders"> & {
|
||||||
noBurger?: boolean;
|
noBurger?: boolean;
|
||||||
children: Children;
|
children: Children;
|
||||||
icon: Children;
|
icon: Children;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const PageHeader = observer(
|
export const PageHeader = observer(
|
||||||
({ children, icon, noBurger }: PageHeaderProps) => {
|
({ children, icon, noBurger, ...props }: PageHeaderProps) => {
|
||||||
const layout = useApplicationState().layout;
|
const layout = useApplicationState().layout;
|
||||||
const visible = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
const visible = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header placement="primary" borders={!visible}>
|
<Header
|
||||||
|
{...props}
|
||||||
|
placement="primary"
|
||||||
|
topBorder={!visible}
|
||||||
|
bottomBorder={!pathname.includes("/server")}>
|
||||||
{!noBurger && <HamburgerAction />}
|
{!noBurger && <HamburgerAction />}
|
||||||
<IconContainer
|
<IconContainer
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
@ -135,7 +155,7 @@ export function HamburgerAction() {
|
||||||
|
|
||||||
function openSidebar() {
|
function openSidebar() {
|
||||||
document
|
document
|
||||||
.querySelector("#app > div > div")
|
.querySelector("#app > div > div > div")
|
||||||
?.scrollTo({ behavior: "smooth", left: 0 });
|
?.scrollTo({ behavior: "smooth", left: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
export default styled.div`
|
export default styled.div`
|
||||||
height: 0px;
|
height: 0;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 8px 10px;
|
margin: 8px 15px;
|
||||||
border-top: 1px solid var(--tertiary-foreground);
|
border-top: 1px solid var(--tertiary-foreground);
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import rgba from "color-rgba";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { createGlobalStyle } from "styled-components";
|
import { createGlobalStyle } from "styled-components";
|
||||||
|
@ -71,6 +72,12 @@ export type Theme = Overrides & {
|
||||||
font?: Fonts;
|
font?: Fonts;
|
||||||
css?: string;
|
css?: string;
|
||||||
monospaceFont?: MonospaceFonts;
|
monospaceFont?: MonospaceFonts;
|
||||||
|
"min-opacity"?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComputedVariables = Theme & {
|
||||||
|
"header-height"?: string;
|
||||||
|
"effective-bottom-offset"?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ThemeOptions {
|
export interface ThemeOptions {
|
||||||
|
@ -287,7 +294,13 @@ 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) => {
|
||||||
|
const colour = rgba(theme[key]);
|
||||||
|
if (colour) {
|
||||||
|
const [r, g, b] = colour;
|
||||||
|
return `--${key}: ${theme[key]}; --${key}-rgb: ${r}, ${g}, ${b};`;
|
||||||
|
} else {
|
||||||
return `--${key}: ${theme[key]};`;
|
return `--${key}: ${theme[key]};`;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
|
/**
|
||||||
|
* Schedule a task at the end of the Event Loop
|
||||||
|
* @param cb Callback
|
||||||
|
*/
|
||||||
export const defer = (cb: () => void) => setTimeout(cb, 0);
|
export const defer = (cb: () => void) => setTimeout(cb, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a task at the end of the second Event Loop
|
||||||
|
* @param cb Callback
|
||||||
|
*/
|
||||||
|
export const chainedDefer = (cb: () => void) => defer(() => defer(cb));
|
||||||
|
|
|
@ -138,6 +138,16 @@ export default class Layout implements Store, Persistent<Data> {
|
||||||
return this.lastHomePath;
|
return this.lastHomePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last path the user had open.
|
||||||
|
* @returns Last path
|
||||||
|
*/
|
||||||
|
@computed getLastPath() {
|
||||||
|
return this.lastSection === "home"
|
||||||
|
? this.lastHomePath
|
||||||
|
: this.getLastOpened(this.lastSection);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current path open in the home tab.
|
* Set the current path open in the home tab.
|
||||||
* @param path Pathname
|
* @param path Pathname
|
||||||
|
|
|
@ -29,6 +29,7 @@ export interface ISettings {
|
||||||
"appearance:emoji": EmojiPack;
|
"appearance:emoji": EmojiPack;
|
||||||
"appearance:ligatures": boolean;
|
"appearance:ligatures": boolean;
|
||||||
"appearance:seasonal": boolean;
|
"appearance:seasonal": boolean;
|
||||||
|
"appearance:transparency": boolean;
|
||||||
|
|
||||||
"appearance:theme:base": "dark" | "light";
|
"appearance:theme:base": "dark" | "light";
|
||||||
"appearance:theme:overrides": Partial<Overrides>;
|
"appearance:theme:overrides": Partial<Overrides>;
|
||||||
|
@ -140,6 +141,7 @@ export default class Settings
|
||||||
if (key === "appearance") {
|
if (key === "appearance") {
|
||||||
this.remove("appearance:emoji");
|
this.remove("appearance:emoji");
|
||||||
this.remove("appearance:seasonal");
|
this.remove("appearance:seasonal");
|
||||||
|
this.remove("appearance:transparency");
|
||||||
} else {
|
} else {
|
||||||
this.remove("appearance:ligatures");
|
this.remove("appearance:ligatures");
|
||||||
this.remove("appearance:theme:base");
|
this.remove("appearance:theme:base");
|
||||||
|
@ -169,6 +171,7 @@ export default class Settings
|
||||||
appearance: this.pullKeys([
|
appearance: this.pullKeys([
|
||||||
"appearance:emoji",
|
"appearance:emoji",
|
||||||
"appearance:seasonal",
|
"appearance:seasonal",
|
||||||
|
"appearance:transparency",
|
||||||
]),
|
]),
|
||||||
theme: this.pullKeys([
|
theme: this.pullKeys([
|
||||||
"appearance:ligatures",
|
"appearance:ligatures",
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import rgba from "color-rgba";
|
||||||
import { makeAutoObservable, computed, action } from "mobx";
|
import { makeAutoObservable, computed, action } from "mobx";
|
||||||
|
|
||||||
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Theme,
|
Theme,
|
||||||
PRESETS,
|
PRESETS,
|
||||||
|
@ -8,6 +11,7 @@ import {
|
||||||
DEFAULT_MONO_FONT,
|
DEFAULT_MONO_FONT,
|
||||||
Fonts,
|
Fonts,
|
||||||
MonospaceFonts,
|
MonospaceFonts,
|
||||||
|
ComputedVariables,
|
||||||
} from "../../../context/Theme";
|
} from "../../../context/Theme";
|
||||||
|
|
||||||
import Settings from "../Settings";
|
import Settings from "../Settings";
|
||||||
|
@ -96,10 +100,10 @@ export default class STheme {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed computeVariables(): Theme {
|
@computed computeVariables(): ComputedVariables {
|
||||||
const variables = this.getVariables() as Record<
|
const variables = this.getVariables() as Record<
|
||||||
string,
|
string,
|
||||||
string | boolean
|
string | boolean | number
|
||||||
>;
|
>;
|
||||||
|
|
||||||
for (const key of Object.keys(variables)) {
|
for (const key of Object.keys(variables)) {
|
||||||
|
@ -109,7 +113,16 @@ export default class STheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return variables as unknown as Theme;
|
return {
|
||||||
|
...(variables as unknown as Theme),
|
||||||
|
"min-opacity": this.settings.get("appearance:transparency", true)
|
||||||
|
? 0
|
||||||
|
: 1,
|
||||||
|
"header-height": isTouchscreenDevice ? "56px" : "48px",
|
||||||
|
"effective-bottom-offset": isTouchscreenDevice
|
||||||
|
? "var(--bottom-navigation-height)"
|
||||||
|
: "0px",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@action setVariable(key: Variables, value: string) {
|
@action setVariable(key: Variables, value: string) {
|
||||||
|
@ -204,15 +217,11 @@ export default class STheme {
|
||||||
function getContrastingColour(hex: string, fallback?: string): string {
|
function getContrastingColour(hex: string, fallback?: string): string {
|
||||||
if (typeof hex !== "string") return "black";
|
if (typeof hex !== "string") return "black";
|
||||||
|
|
||||||
// TODO: Switch to color-parse
|
const colour = rgba(hex);
|
||||||
// Try parse hex value.
|
if (!colour) return fallback ? getContrastingColour(fallback) : "black";
|
||||||
hex = hex.replace(/#/g, "");
|
|
||||||
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))
|
const [r, g, b] = colour;
|
||||||
return fallback ? getContrastingColour(fallback) : "black";
|
return (r / 255) * 0.299 + (g / 255) * 0.587 + (b / 255) * 0.114 >= 0.186
|
||||||
|
? "black"
|
||||||
return r * 0.299 + g * 0.587 + b * 0.114 >= 0.186 ? "black" : "white";
|
: "white";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels";
|
import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels";
|
||||||
import { Switch, Route, useLocation } from "react-router-dom";
|
import { Switch, Route, useLocation } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import ContextMenus from "../lib/ContextMenus";
|
import ContextMenus from "../lib/ContextMenus";
|
||||||
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../mobx/State";
|
||||||
|
import { SIDEBAR_CHANNELS } from "../mobx/stores/Layout";
|
||||||
|
|
||||||
import Popovers from "../context/intermediate/Popovers";
|
import Popovers from "../context/intermediate/Popovers";
|
||||||
import Notifications from "../context/revoltjs/Notifications";
|
import Notifications from "../context/revoltjs/Notifications";
|
||||||
import StateMonitor from "../context/revoltjs/StateMonitor";
|
import StateMonitor from "../context/revoltjs/StateMonitor";
|
||||||
import SyncManager from "../context/revoltjs/SyncManager";
|
|
||||||
|
|
||||||
import { Titlebar } from "../components/native/Titlebar";
|
import { Titlebar } from "../components/native/Titlebar";
|
||||||
import BottomNavigation from "../components/navigation/BottomNavigation";
|
import BottomNavigation from "../components/navigation/BottomNavigation";
|
||||||
|
@ -24,12 +26,54 @@ import ChannelSettings from "./settings/ChannelSettings";
|
||||||
import ServerSettings from "./settings/ServerSettings";
|
import ServerSettings from "./settings/ServerSettings";
|
||||||
import Settings from "./settings/Settings";
|
import Settings from "./settings/Settings";
|
||||||
|
|
||||||
const Routes = styled.div`
|
const AppContainer = styled.div`
|
||||||
|
background-size: cover !important;
|
||||||
|
background-position: center center !important;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StatusBar = styled.div`
|
||||||
|
height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid white;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Routes = styled.div.attrs({ "data-component": "routes" })<{
|
||||||
|
borders: boolean;
|
||||||
|
}>`
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
background: var(--primary-background);
|
background: var(--primary-background);
|
||||||
|
|
||||||
|
/*background-color: rgba(
|
||||||
|
var(--primary-background-rgb),
|
||||||
|
max(var(--min-opacity), 0.75)
|
||||||
|
);*/
|
||||||
|
//backdrop-filter: blur(10px);
|
||||||
|
|
||||||
|
${() =>
|
||||||
|
isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
overflow: hidden;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.borders &&
|
||||||
|
css`
|
||||||
|
border-start-start-radius: 8px;
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
@ -37,6 +81,7 @@ export default function App() {
|
||||||
const fixedBottomNav =
|
const fixedBottomNav =
|
||||||
path === "/" || path === "/settings" || path.startsWith("/friends");
|
path === "/" || path === "/settings" || path.startsWith("/friends");
|
||||||
const inChannel = path.includes("/channel");
|
const inChannel = path.includes("/channel");
|
||||||
|
const inServer = path.includes("/server");
|
||||||
const inSpecial =
|
const inSpecial =
|
||||||
(path.startsWith("/friends") && isTouchscreenDevice) ||
|
(path.startsWith("/friends") && isTouchscreenDevice) ||
|
||||||
path.startsWith("/invite") ||
|
path.startsWith("/invite") ||
|
||||||
|
@ -44,6 +89,11 @@ export default function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/*<StatusBar>
|
||||||
|
<div className="title">Planned outage: CDN (~2 hours)</div>
|
||||||
|
<div className="button">View status</div>
|
||||||
|
</StatusBar>*/}
|
||||||
|
<AppContainer>
|
||||||
{window.isNative && !window.native.getConfig().frame && (
|
{window.isNative && !window.native.getConfig().frame && (
|
||||||
<Titlebar />
|
<Titlebar />
|
||||||
)}
|
)}
|
||||||
|
@ -70,7 +120,7 @@ export default function App() {
|
||||||
height: 50,
|
height: 50,
|
||||||
}}
|
}}
|
||||||
docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
|
docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
|
||||||
<Routes>
|
<Routes borders={inServer}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
path="/server/:server/channel/:channel/settings/:page"
|
path="/server/:server/channel/:channel/settings/:page"
|
||||||
|
@ -111,9 +161,15 @@ export default function App() {
|
||||||
component={Channel}
|
component={Channel}
|
||||||
/>
|
/>
|
||||||
<Route path="/server/:server" component={Channel} />
|
<Route path="/server/:server" component={Channel} />
|
||||||
<Route path="/channel/:channel" component={Channel} />
|
<Route
|
||||||
|
path="/channel/:channel"
|
||||||
|
component={Channel}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route path="/settings/:page" component={Settings} />
|
<Route
|
||||||
|
path="/settings/:page"
|
||||||
|
component={Settings}
|
||||||
|
/>
|
||||||
<Route path="/settings" component={Settings} />
|
<Route path="/settings" component={Settings} />
|
||||||
|
|
||||||
<Route path="/dev" component={Developer} />
|
<Route path="/dev" component={Developer} />
|
||||||
|
@ -128,6 +184,7 @@ export default function App() {
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<StateMonitor />
|
<StateMonitor />
|
||||||
</OverlappingPanels>
|
</OverlappingPanels>
|
||||||
|
</AppContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import ChannelHeader from "./ChannelHeader";
|
||||||
import { MessageArea } from "./messaging/MessageArea";
|
import { MessageArea } from "./messaging/MessageArea";
|
||||||
import VoiceHeader from "./voice/VoiceHeader";
|
import VoiceHeader from "./voice/VoiceHeader";
|
||||||
|
|
||||||
const ChannelMain = styled.div`
|
const ChannelMain = styled.div.attrs({ "data-component": "channel" })`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
@ -37,7 +37,9 @@ const ChannelMain = styled.div`
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChannelContent = styled.div`
|
const ChannelContent = styled.div.attrs({
|
||||||
|
"data-component": "content",
|
||||||
|
})`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -78,7 +78,6 @@ const Info = styled.div`
|
||||||
|
|
||||||
export default observer(({ channel }: ChannelHeaderProps) => {
|
export default observer(({ channel }: ChannelHeaderProps) => {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const layout = useApplicationState().layout;
|
|
||||||
|
|
||||||
const name = getChannelName(channel);
|
const name = getChannelName(channel);
|
||||||
let icon, recipient: User | undefined;
|
let icon, recipient: User | undefined;
|
||||||
|
@ -99,7 +98,7 @@ export default observer(({ channel }: ChannelHeaderProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader icon={icon}>
|
<PageHeader icon={icon} transparent>
|
||||||
<Info>
|
<Info>
|
||||||
<span className="name">{name}</span>
|
<span className="name">{name}</span>
|
||||||
{isTouchscreenDevice &&
|
{isTouchscreenDevice &&
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
|
import { chainedDefer, defer } from "../../../lib/defer";
|
||||||
import { internalEmit } from "../../../lib/eventEmitter";
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||||
|
@ -29,20 +30,34 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
function openRightSidebar() {
|
function slideOpen() {
|
||||||
const panels = document.querySelector("#app > div > div");
|
if (!isTouchscreenDevice) return;
|
||||||
|
const panels = document.querySelector("#app > div > div > div");
|
||||||
panels?.scrollTo({
|
panels?.scrollTo({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
left: panels.clientWidth * 3,
|
left: panels.clientWidth * 3,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openSidebar() {
|
function openSearch() {
|
||||||
if (isTouchscreenDevice) {
|
if (
|
||||||
openRightSidebar();
|
!isTouchscreenDevice &&
|
||||||
} else {
|
!layout.getSectionState(SIDEBAR_MEMBERS, true)
|
||||||
|
) {
|
||||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slideOpen();
|
||||||
|
chainedDefer(() => internalEmit("RightSidebar", "open", "search"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMembers() {
|
||||||
|
if (!isTouchscreenDevice) {
|
||||||
|
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
slideOpen();
|
||||||
|
chainedDefer(() => internalEmit("RightSidebar", "open", undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -74,17 +89,13 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) {
|
||||||
)}
|
)}
|
||||||
<VoiceActions channel={channel} />
|
<VoiceActions channel={channel} />
|
||||||
{channel.channel_type !== "VoiceChannel" && (
|
{channel.channel_type !== "VoiceChannel" && (
|
||||||
<IconButton
|
<IconButton onClick={openSearch}>
|
||||||
onClick={() => {
|
|
||||||
internalEmit("RightSidebar", "open", "search");
|
|
||||||
openRightSidebar();
|
|
||||||
}}>
|
|
||||||
<Search size={25} />
|
<Search size={25} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
{(channel.channel_type === "Group" ||
|
{(channel.channel_type === "Group" ||
|
||||||
channel.channel_type === "TextChannel") && (
|
channel.channel_type === "TextChannel") && (
|
||||||
<IconButton onClick={openSidebar}>
|
<IconButton onClick={openMembers}>
|
||||||
<Group size={25} />
|
<Group size={25} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -33,13 +33,18 @@ import Preloader from "../../../components/ui/Preloader";
|
||||||
import ConversationStart from "./ConversationStart";
|
import ConversationStart from "./ConversationStart";
|
||||||
import MessageRenderer from "./MessageRenderer";
|
import MessageRenderer from "./MessageRenderer";
|
||||||
|
|
||||||
const Area = styled.div`
|
const Area = styled.div.attrs({ "data-scroll-offset": "with-padding" })`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
word-break: break-word;
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -30,6 +30,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const VoiceBase = styled.div`
|
const VoiceBase = styled.div`
|
||||||
|
margin-top: 48px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
.list {
|
.list {
|
||||||
padding: 0 10px 10px 10px;
|
padding: 0 10px 10px 10px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
overflow-y: scroll;
|
|
||||||
|
|
||||||
&[data-empty="true"] {
|
&[data-empty="true"] {
|
||||||
img {
|
img {
|
||||||
|
@ -185,12 +184,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide the remove friend button on smaller screens.
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
.list {
|
.list .remove {
|
||||||
padding: 0 8px 8px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,10 @@ import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { RelationshipStatus, Presence } from "revolt-api/types/Users";
|
import { RelationshipStatus, Presence } from "revolt-api/types/Users";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import styles from "./Friend.module.scss";
|
import styles from "./Friend.module.scss";
|
||||||
|
import classNames from "classnames";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
|
||||||
import { TextReact } from "../../lib/i18n";
|
import { TextReact } from "../../lib/i18n";
|
||||||
|
@ -16,7 +18,7 @@ import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
import CollapsibleSection from "../../components/common/CollapsibleSection";
|
import CollapsibleSection from "../../components/common/CollapsibleSection";
|
||||||
import Tooltip from "../../components/common/Tooltip";
|
import Tooltip from "../../components/common/Tooltip";
|
||||||
import UserIcon from "../../components/common/user/UserIcon";
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
import Header, { PageHeader } from "../../components/ui/Header";
|
import { PageHeader } from "../../components/ui/Header";
|
||||||
import IconButton from "../../components/ui/IconButton";
|
import IconButton from "../../components/ui/IconButton";
|
||||||
|
|
||||||
import { Children } from "../../types/Preact";
|
import { Children } from "../../types/Preact";
|
||||||
|
@ -72,7 +74,7 @@ export default observer(() => {
|
||||||
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
|
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader icon={<UserDetail size={24} />} noBurger>
|
<PageHeader icon={<UserDetail size={24} />} transparent noBurger>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<Text id="app.navigation.tabs.friends" />
|
<Text id="app.navigation.tabs.friends" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,8 +117,9 @@ export default observer(() => {
|
||||||
*/}
|
*/}
|
||||||
</div>
|
</div>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
<div data-scroll-offset="true" data-avoids-navigation="true">
|
||||||
<div
|
<div
|
||||||
className={styles.list}
|
className={classNames(styles.list, "with-padding")}
|
||||||
data-empty={isEmpty}
|
data-empty={isEmpty}
|
||||||
data-mobile={isTouchscreenDevice}>
|
data-mobile={isTouchscreenDevice}>
|
||||||
{isEmpty && (
|
{isEmpty && (
|
||||||
|
@ -144,7 +147,10 @@ export default observer(() => {
|
||||||
size={64}
|
size={64}
|
||||||
mask={
|
mask={
|
||||||
i <
|
i <
|
||||||
Math.min(incoming.length - 1, 2)
|
Math.min(
|
||||||
|
incoming.length - 1,
|
||||||
|
2,
|
||||||
|
)
|
||||||
? "url(#overlap)"
|
? "url(#overlap)"
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
@ -209,6 +215,7 @@ export default observer(() => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 95%;
|
height: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
@ -30,15 +30,11 @@
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
max-width: 650px;
|
max-width: 650px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:nth-child(4) {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
div {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ export default observer(() => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<PageHeader icon={<HomeIcon size={24} />}>
|
<PageHeader icon={<HomeIcon size={24} />} transparent>
|
||||||
<Text id="app.navigation.tabs.home" />
|
<Text id="app.navigation.tabs.home" />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className={styles.homeScreen}>
|
<div className={styles.homeScreen}>
|
||||||
|
@ -103,8 +103,8 @@ export default observer(() => {
|
||||||
Create a group
|
Create a group
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
{/*<a
|
||||||
href="https://revolt.social"
|
href="#"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<CategoryButton
|
<CategoryButton
|
||||||
|
@ -115,7 +115,7 @@ export default observer(() => {
|
||||||
}>
|
}>
|
||||||
Join a community
|
Join a community
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
</a>
|
</a>*/}
|
||||||
|
|
||||||
{client.servers.get(
|
{client.servers.get(
|
||||||
"01F7ZSBSFHQ8TA81725KQCSDDP",
|
"01F7ZSBSFHQ8TA81725KQCSDDP",
|
||||||
|
@ -159,15 +159,16 @@ export default observer(() => {
|
||||||
rel="noreferrer">
|
rel="noreferrer">
|
||||||
<CategoryButton
|
<CategoryButton
|
||||||
action="external"
|
action="external"
|
||||||
|
description={
|
||||||
|
"Support the project by donating - thank you!"
|
||||||
|
}
|
||||||
icon={<Money size={32} />}>
|
icon={<Money size={32} />}>
|
||||||
<Text id="app.home.donate" />
|
<Text id="app.home.donate" />
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={<Text id="app.home.settings-tooltip" />}>
|
||||||
<Text id="app.home.settings-tooltip" />
|
|
||||||
}>
|
|
||||||
<Link to="/settings">
|
<Link to="/settings">
|
||||||
<CategoryButton
|
<CategoryButton
|
||||||
action="chevron"
|
action="chevron"
|
||||||
|
@ -176,7 +177,6 @@ export default observer(() => {
|
||||||
</CategoryButton>
|
</CategoryButton>
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
|
||||||
{isDecember && (
|
{isDecember && (
|
||||||
<a href="#" onClick={toggleSeasonalTheme}>
|
<a href="#" onClick={toggleSeasonalTheme}>
|
||||||
Turn {seasonalTheme ? "off" : "on"} homescreen
|
Turn {seasonalTheme ? "off" : "on"} homescreen
|
||||||
|
|
|
@ -102,7 +102,7 @@ export function GenericSettings({
|
||||||
/>
|
/>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{isTouchscreenDevice && (
|
{isTouchscreenDevice && (
|
||||||
<Header placement="primary">
|
<Header placement="primary" transparent>
|
||||||
{typeof page === "undefined" ? (
|
{typeof page === "undefined" ? (
|
||||||
<>
|
<>
|
||||||
{showExitButton && (
|
{showExitButton && (
|
||||||
|
@ -168,6 +168,9 @@ export function GenericSettings({
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div
|
<div
|
||||||
className={styles.scrollbox}
|
className={styles.scrollbox}
|
||||||
|
data-scroll-offset={
|
||||||
|
isTouchscreenDevice ? "with-padding" : undefined
|
||||||
|
}
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
// Force scroll to top if page changes.
|
// Force scroll to top if page changes.
|
||||||
if (ref) {
|
if (ref) {
|
||||||
|
|
|
@ -43,18 +43,13 @@
|
||||||
background: var(--primary-background);
|
background: var(--primary-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbox {
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar */
|
/* Sidebar */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 20px 8px calc(var(--bottom-navigation-height) + 30px);
|
padding: calc(var(--header-height) + 4px) 8px
|
||||||
|
calc(var(--bottom-navigation-height) + 30px);
|
||||||
min-width: 218px;
|
min-width: 218px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +71,7 @@
|
||||||
|
|
||||||
.contentcontainer {
|
.contentcontainer {
|
||||||
max-width: unset !important;
|
max-width: unset !important;
|
||||||
padding: 16px 12px var(--bottom-navigation-height) !important;
|
padding: 72px 12px var(--bottom-navigation-height) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,6 +112,7 @@
|
||||||
|
|
||||||
// All children receive custom scrollbar.
|
// All children receive custom scrollbar.
|
||||||
> * > ::-webkit-scrollbar-thumb {
|
> * > ::-webkit-scrollbar-thumb {
|
||||||
|
min-height: 100px;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
background-clip: content-box;
|
background-clip: content-box;
|
||||||
border-top: 80px solid transparent;
|
border-top: 80px solid transparent;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
Speaker,
|
Speaker,
|
||||||
Store,
|
Store,
|
||||||
Bot,
|
Bot,
|
||||||
|
Trash,
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Route, Switch, useHistory } from "react-router-dom";
|
import { Route, Switch, useHistory } from "react-router-dom";
|
||||||
|
@ -24,16 +25,19 @@ import { LIBRARY_VERSION } from "revolt.js";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import styles from "./Settings.module.scss";
|
import styles from "./Settings.module.scss";
|
||||||
|
import { openContextMenu } from "preact-context-menu";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
|
|
||||||
|
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
||||||
import RequiresOnline from "../../context/revoltjs/RequiresOnline";
|
import RequiresOnline from "../../context/revoltjs/RequiresOnline";
|
||||||
import { AppContext, LogOutContext } from "../../context/revoltjs/RevoltClient";
|
import { AppContext, LogOutContext } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import UserIcon from "../../components/common/user/UserIcon";
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
import { Username } from "../../components/common/user/UserShort";
|
import { Username } from "../../components/common/user/UserShort";
|
||||||
|
import UserStatus from "../../components/common/user/UserStatus";
|
||||||
import LineDivider from "../../components/ui/LineDivider";
|
import LineDivider from "../../components/ui/LineDivider";
|
||||||
|
|
||||||
import ButtonItem from "../../components/navigation/items/ButtonItem";
|
import ButtonItem from "../../components/navigation/items/ButtonItem";
|
||||||
|
@ -54,19 +58,67 @@ import { Sessions } from "./panes/Sessions";
|
||||||
import { Sync } from "./panes/Sync";
|
import { Sync } from "./panes/Sync";
|
||||||
import { ThemeShop } from "./panes/ThemeShop";
|
import { ThemeShop } from "./panes/ThemeShop";
|
||||||
|
|
||||||
const IndexHeader = styled.div`
|
const AccountHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.account {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
border-radius: var(--border-radius);
|
|
||||||
padding: 20px;
|
.details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 12px;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusChanger {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
background: var(--tertiary-background);
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding-inline-start: 12px;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 48px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
const logout = useContext(LogOutContext);
|
const logout = useContext(LogOutContext);
|
||||||
|
const { openScreen } = useIntermediate();
|
||||||
const experiments = useApplicationState().experiments;
|
const experiments = useApplicationState().experiments;
|
||||||
|
|
||||||
function switchPage(to?: string) {
|
function switchPage(to?: string) {
|
||||||
|
@ -270,10 +322,40 @@ export default observer(() => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
indexHeader={
|
indexHeader={
|
||||||
<IndexHeader>
|
<AccountHeader>
|
||||||
<UserIcon size={64} target={client.user!} />
|
<div className="account">
|
||||||
|
<UserIcon
|
||||||
|
size={64}
|
||||||
|
target={client.user!}
|
||||||
|
status
|
||||||
|
onClick={() => openContextMenu("Status")}
|
||||||
|
/>
|
||||||
|
<div className="details">
|
||||||
<Username user={client.user!} prefixAt />
|
<Username user={client.user!} prefixAt />
|
||||||
</IndexHeader>
|
<UserStatus user={client.user!} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="statusChanger">
|
||||||
|
<a
|
||||||
|
className="status"
|
||||||
|
onClick={() =>
|
||||||
|
openScreen({
|
||||||
|
id: "special_input",
|
||||||
|
type: "set_custom_status",
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
Change your status...
|
||||||
|
</a>
|
||||||
|
{client.user!.status?.text && (
|
||||||
|
<Trash
|
||||||
|
size={24}
|
||||||
|
onClick={() =>
|
||||||
|
client.users.edit({ remove: "StatusText" })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</AccountHeader>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
DisplayEmojiShim,
|
DisplayEmojiShim,
|
||||||
ThemeCustomCSSShim,
|
ThemeCustomCSSShim,
|
||||||
DisplaySeasonalShim,
|
DisplaySeasonalShim,
|
||||||
|
DisplayTransparencyShim,
|
||||||
} from "../../../components/settings/AppearanceShims";
|
} from "../../../components/settings/AppearanceShims";
|
||||||
import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides";
|
import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides";
|
||||||
import ThemeTools from "../../../components/settings/appearance/ThemeTools";
|
import ThemeTools from "../../../components/settings/appearance/ThemeTools";
|
||||||
|
@ -27,6 +28,10 @@ export const Appearance = observer(() => {
|
||||||
<hr />
|
<hr />
|
||||||
<ThemeAccentShim />
|
<ThemeAccentShim />
|
||||||
<hr />
|
<hr />
|
||||||
|
<h3>
|
||||||
|
<Text id="app.settings.pages.appearance.theme_options.title" />
|
||||||
|
</h3>
|
||||||
|
<DisplayTransparencyShim />
|
||||||
<DisplaySeasonalShim />
|
<DisplaySeasonalShim />
|
||||||
<hr />
|
<hr />
|
||||||
<DisplayFontShim />
|
<DisplayFontShim />
|
||||||
|
|
|
@ -126,7 +126,6 @@ export const Overview = observer(({ server }: Props) => {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}>
|
}}>
|
||||||
<span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
|
<span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
|
||||||
<span>Sends a message when someone joins your server</span>
|
|
||||||
<ComboBox
|
<ComboBox
|
||||||
value={
|
value={
|
||||||
systemMessages?.[
|
systemMessages?.[
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
.preact-context-menu .context-menu {
|
.preact-context-menu .context-menu {
|
||||||
z-index: 5000;
|
z-index: 10000;
|
||||||
min-width: 190px;
|
min-width: 190px;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
background: var(--primary-background) !important;
|
//background: var(--primary-background) !important;
|
||||||
|
background-color: rgba(
|
||||||
|
var(--primary-background-rgb),
|
||||||
|
max(var(--min-opacity), 0.8)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
box-shadow: 0px 0px 8px 8px rgba(0, 0, 0, 0.05);
|
box-shadow: 0px 0px 8px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
> span,
|
> span,
|
||||||
|
@ -23,7 +28,11 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--secondary-background);
|
background-color: rgba(
|
||||||
|
var(--secondary-background-rgb),
|
||||||
|
max(var(--min-opacity), 0.75)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,32 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
|
min-height: 30px;
|
||||||
|
min-width: 30px;
|
||||||
|
|
||||||
|
background-clip: content-box;
|
||||||
background: var(--scrollbar-thumb);
|
background: var(--scrollbar-thumb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-scroll-offset] {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-scroll-offset="with-padding"],
|
||||||
|
[data-scroll-offset] .with-padding {
|
||||||
|
padding-top: var(--header-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-scroll-offset]::-webkit-scrollbar-thumb {
|
||||||
|
background-clip: content-box;
|
||||||
|
border-top: var(--header-height) solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-avoids-navigation]::-webkit-scrollbar-thumb {
|
||||||
|
background-clip: content-box;
|
||||||
|
border-bottom: var(--effective-bottom-offset) solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
::-webkit-scrollbar-corner {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
--input-border-width: 2px;
|
--input-border-width: 2px;
|
||||||
--textarea-padding: 16px;
|
--textarea-padding: 16px;
|
||||||
--textarea-line-height: 20px;
|
--textarea-line-height: 20px;
|
||||||
--message-box-padding: 14px 14px 14px 0;
|
--message-box-padding: 14px;
|
||||||
|
|
||||||
--attachment-max-width: 480px;
|
--attachment-max-width: 480px;
|
||||||
--attachment-max-height: 640px;
|
--attachment-max-height: 640px;
|
||||||
|
|
47
yarn.lock
47
yarn.lock
|
@ -1303,6 +1303,11 @@
|
||||||
javascript-natural-sort "0.7.1"
|
javascript-natural-sort "0.7.1"
|
||||||
lodash "4.17.21"
|
lodash "4.17.21"
|
||||||
|
|
||||||
|
"@types/color-rgba@^2.1.0":
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/color-rgba/-/color-rgba-2.1.0.tgz#0182795370deae5c2c62f71ea6e91c6bab87394d"
|
||||||
|
integrity sha512-tWcJLEiKdZ3ihJdThfLCe6Kw5vo0lgGcuucGkbtzcp1zifDA1E2Z96wxeSS/r+ytpHD15NCAWabX8GV911ywCA==
|
||||||
|
|
||||||
"@types/debug@^4.1.6":
|
"@types/debug@^4.1.6":
|
||||||
version "4.1.7"
|
version "4.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
|
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
|
||||||
|
@ -1642,6 +1647,11 @@ ajv@^8.0.1, ajv@^8.6.0:
|
||||||
require-from-string "^2.0.2"
|
require-from-string "^2.0.2"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
almost-equal@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/almost-equal/-/almost-equal-1.1.0.tgz#f851c631138757994276aa2efbe8dfa3066cccdd"
|
||||||
|
integrity sha1-+FHGMROHV5lCdqou++jfowZszN0=
|
||||||
|
|
||||||
ansi-colors@^4.1.1:
|
ansi-colors@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||||
|
@ -1934,11 +1944,34 @@ color-name@1.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||||
|
|
||||||
color-name@~1.1.4:
|
color-name@^1.0.0, color-name@~1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
|
color-parse@^1.4.1:
|
||||||
|
version "1.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5"
|
||||||
|
integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA==
|
||||||
|
dependencies:
|
||||||
|
color-name "^1.0.0"
|
||||||
|
|
||||||
|
color-rgba@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-rgba/-/color-rgba-2.3.0.tgz#d5eb481d7933d2542d1f222ea10ad40d159e9d35"
|
||||||
|
integrity sha512-z/5fMOY8/IzrBHPBk+n3ATNSM/1atXcHCRPTGPLlzYJ4fn7CRD46zzt3lkLtQ44cL8UIUU4JBXDVrhWj1khiwg==
|
||||||
|
dependencies:
|
||||||
|
color-parse "^1.4.1"
|
||||||
|
color-space "^1.14.6"
|
||||||
|
|
||||||
|
color-space@^1.14.6:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-space/-/color-space-1.16.0.tgz#611781bca41cd8582a1466fd9e28a7d3d89772a2"
|
||||||
|
integrity sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg==
|
||||||
|
dependencies:
|
||||||
|
hsluv "^0.0.3"
|
||||||
|
mumath "^3.3.4"
|
||||||
|
|
||||||
colorette@^1.3.0:
|
colorette@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
|
||||||
|
@ -2742,6 +2775,11 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-
|
||||||
dependencies:
|
dependencies:
|
||||||
react-is "^16.7.0"
|
react-is "^16.7.0"
|
||||||
|
|
||||||
|
hsluv@^0.0.3:
|
||||||
|
version "0.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/hsluv/-/hsluv-0.0.3.tgz#829107dafb4a9f8b52a1809ed02e091eade6754c"
|
||||||
|
integrity sha1-gpEH2vtKn4tSoYCe0C4JHq3mdUw=
|
||||||
|
|
||||||
idb@^6.0.0:
|
idb@^6.0.0:
|
||||||
version "6.1.2"
|
version "6.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.2.tgz#82ef5c951b8e1f47875d36ccafa4bedafc62f2f1"
|
resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.2.tgz#82ef5c951b8e1f47875d36ccafa4bedafc62f2f1"
|
||||||
|
@ -3276,6 +3314,13 @@ ms@2.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
mumath@^3.3.4:
|
||||||
|
version "3.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/mumath/-/mumath-3.3.4.tgz#48d4a0f0fd8cad4e7b32096ee89b161a63d30bbf"
|
||||||
|
integrity sha1-SNSg8P2MrU57Mglu6JsWGmPTC78=
|
||||||
|
dependencies:
|
||||||
|
almost-equal "^1.1.0"
|
||||||
|
|
||||||
nanoid@^3.1.30:
|
nanoid@^3.1.30:
|
||||||
version "3.1.30"
|
version "3.1.30"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
|
||||||
|
|
Loading…
Reference in a new issue