Merge branch 'mobx'

This commit is contained in:
Paul
2021-12-24 11:45:49 +00:00
115 changed files with 3973 additions and 3311 deletions

View File

@@ -1,21 +1,21 @@
import { Hash } from "@styled-icons/boxicons-regular";
import { Ghost } from "@styled-icons/boxicons-solid";
import { reaction } from "mobx";
import { observer } from "mobx-react-lite";
import { useParams } from "react-router-dom";
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
import styled from "styled-components";
import { Text } from "preact-i18n";
import { useState } from "preact/hooks";
import { useEffect } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { dispatch, getState } from "../../redux";
import { useApplicationState } from "../../mobx/State";
import { SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
import { useClient } from "../../context/revoltjs/RevoltClient";
import { Hash } from "@styled-icons/boxicons-regular";
import { Ghost } from "@styled-icons/boxicons-solid";
import AgeGate from "../../components/common/AgeGate";
import MessageBox from "../../components/common/messaging/MessageBox";
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
@@ -52,19 +52,19 @@ const PlaceholderBase = styled.div`
justify-content: center;
text-align: center;
margin: auto;
.primary {
color: var(--secondary-foreground);
font-weight: 700;
font-size: 22px;
margin: 0 0 5px 0;
}
.secondary {
color: var(--tertiary-foreground);
font-weight: 400;
}
svg {
margin: 2em auto;
fill-opacity: 0.8;
@@ -84,17 +84,26 @@ export function Channel({ id }: { id: string }) {
return <TextChannel channel={channel} />;
}
const MEMBERS_SIDEBAR_KEY = "sidebar_members";
const CHANNELS_SIDEBAR_KEY = "sidebar_channels";
const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
const [showMembers, setMembers] = useState(
getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true,
);
const [showChannels, setChannels] = useState(
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
);
const layout = useApplicationState().layout;
// Mark channel as read.
useEffect(() => {
const checkUnread = () =>
channel.unread &&
channel.client.unreads!.markRead(
channel._id,
channel.last_message_id!,
true,
);
checkUnread();
return reaction(
() => channel.last_message_id,
() => checkUnread(),
);
}, [channel]);
const id = channel._id;
return (
<AgeGate
type="channel"
@@ -106,54 +115,19 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
channel.nsfw
)
}>
<ChannelHeader
channel={channel}
toggleSidebar={() => {
setMembers(!showMembers);
if (showMembers) {
dispatch({
type: "SECTION_TOGGLE_SET",
id: MEMBERS_SIDEBAR_KEY,
state: false,
});
} else {
dispatch({
type: "SECTION_TOGGLE_UNSET",
id: MEMBERS_SIDEBAR_KEY,
});
}
}}
toggleChannelSidebar={() => {
if (isTouchscreenDevice) {
return
}
setChannels(!showChannels);
if (showChannels) {
dispatch({
type: "SECTION_TOGGLE_SET",
id: CHANNELS_SIDEBAR_KEY,
state: false,
});
} else {
dispatch({
type: "SECTION_TOGGLE_UNSET",
id: CHANNELS_SIDEBAR_KEY,
});
}
}}
/>
<ChannelHeader channel={channel} />
<ChannelMain>
<ChannelContent>
<VoiceHeader id={id} />
<VoiceHeader id={channel._id} />
<MessageArea channel={channel} />
<TypingIndicator channel={channel} />
<JumpToBottom channel={channel} />
<MessageBox channel={channel} />
</ChannelContent>
{!isTouchscreenDevice && showMembers && <RightSidebar />}
{!isTouchscreenDevice &&
layout.getSectionState(SIDEBAR_MEMBERS, true) && (
<RightSidebar />
)}
</ChannelMain>
</AgeGate>
);
@@ -173,13 +147,19 @@ function ChannelPlaceholder() {
<PlaceholderBase>
<Header placement="primary">
<Hash size={24} />
<span className="name"><Text id="app.main.channel.errors.nochannel" /></span>
<span className="name">
<Text id="app.main.channel.errors.nochannel" />
</span>
</Header>
<div className="placeholder">
<Ghost width={80} />
<div className="primary"><Text id="app.main.channel.errors.title" /></div>
<div className="secondary"><Text id="app.main.channel.errors.nochannels" /></div>
<div className="primary">
<Text id="app.main.channel.errors.title" />
</div>
<div className="secondary">
<Text id="app.main.channel.errors.nochannels" />
</div>
</div>
</PlaceholderBase>
);

View File

@@ -1,4 +1,4 @@
import { At, Hash, Menu, ChevronLeft } from "@styled-icons/boxicons-regular";
import { At, Hash } from "@styled-icons/boxicons-regular";
import { Notepad, Group } from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import { Channel } from "revolt.js/dist/maps/Channels";
@@ -7,6 +7,9 @@ import styled, { css } from "styled-components";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State";
import { SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
import { useIntermediate } from "../../context/intermediate/Intermediate";
import { getChannelName } from "../../context/revoltjs/util";
@@ -65,7 +68,7 @@ const Info = styled.div`
}
`;
const IconContainer = styled.div`
const IconConainer = styled.div`
display: flex;
align-items: center;
cursor: pointer;
@@ -84,84 +87,81 @@ const IconContainer = styled.div`
`}
`;
export default observer(
({ channel, toggleSidebar, toggleChannelSidebar }: ChannelHeaderProps) => {
const { openScreen } = useIntermediate();
export default observer(({ channel }: ChannelHeaderProps) => {
const { openScreen } = useIntermediate();
const layout = useApplicationState().layout;
const name = getChannelName(channel);
let icon, recipient: User | undefined;
switch (channel.channel_type) {
case "SavedMessages":
icon = <Notepad size={24} />;
break;
case "DirectMessage":
icon = <At size={24} />;
recipient = channel.recipient;
break;
case "Group":
icon = <Group size={24} />;
break;
case "TextChannel":
icon = <Hash size={24} />;
break;
}
const name = getChannelName(channel);
let icon, recipient: User | undefined;
switch (channel.channel_type) {
case "SavedMessages":
icon = <Notepad size={24} />;
break;
case "DirectMessage":
icon = <At size={24} />;
recipient = channel.recipient;
break;
case "Group":
icon = <Group size={24} />;
break;
case "TextChannel":
icon = <Hash size={24} />;
break;
}
return (
<Header placement="primary">
<HamburgerAction />
<IconContainer onClick={toggleChannelSidebar}>
{/*isTouchscreenDevice && <ChevronLeft size={18} /> FIXME: requires mobx merge */}
{icon}
</IconContainer>
<Info>
<span className="name">{name}</span>
{isTouchscreenDevice &&
channel.channel_type === "DirectMessage" && (
<>
<div className="divider" />
<span className="desc">
<div
className="status"
style={{
backgroundColor:
useStatusColour(recipient),
}}
/>
<UserStatus user={recipient} />
</span>
</>
)}
{!isTouchscreenDevice &&
(channel.channel_type === "Group" ||
channel.channel_type === "TextChannel") &&
channel.description && (
<>
<div className="divider" />
<span
className="desc"
onClick={() =>
openScreen({
id: "channel_info",
channel,
})
}>
<Markdown
content={
channel.description.split(
"\n",
)[0] ?? ""
}
disallowBigEmoji
/>
</span>
</>
)}
</Info>
<HeaderActions
channel={channel}
toggleSidebar={toggleSidebar}
/>
</Header>
);
},
);
return (
<Header placement="primary">
<HamburgerAction />
<IconConainer
onClick={() =>
layout.toggleSectionState(SIDEBAR_MEMBERS, true)
}>
{/*isTouchscreenDevice && <ChevronLeft size={18} /> FIXME: requires mobx merge */}
{icon}
</IconConainer>
<Info>
<span className="name">{name}</span>
{isTouchscreenDevice &&
channel.channel_type === "DirectMessage" && (
<>
<div className="divider" />
<span className="desc">
<div
className="status"
style={{
backgroundColor:
useStatusColour(recipient),
}}
/>
<UserStatus user={recipient} />
</span>
</>
)}
{!isTouchscreenDevice &&
(channel.channel_type === "Group" ||
channel.channel_type === "TextChannel") &&
channel.description && (
<>
<div className="divider" />
<span
className="desc"
onClick={() =>
openScreen({
id: "channel_info",
channel,
})
}>
<Markdown
content={
channel.description.split("\n")[0] ?? ""
}
disallowBigEmoji
/>
</span>
</>
)}
</Info>
<HeaderActions channel={channel} />
</Header>
);
});

View File

@@ -14,6 +14,9 @@ import { internalEmit } from "../../../lib/eventEmitter";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
import { useApplicationState } from "../../../mobx/State";
import { SIDEBAR_MEMBERS } from "../../../mobx/stores/Layout";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import UpdateIndicator from "../../../components/common/UpdateIndicator";
@@ -21,10 +24,8 @@ import IconButton from "../../../components/ui/IconButton";
import { ChannelHeaderProps } from "../ChannelHeader";
export default function HeaderActions({
channel,
toggleSidebar,
}: ChannelHeaderProps) {
export default function HeaderActions({ channel }: ChannelHeaderProps) {
const layout = useApplicationState().layout;
const { openScreen } = useIntermediate();
const history = useHistory();
@@ -40,7 +41,7 @@ export default function HeaderActions({
if (isTouchscreenDevice) {
openRightSidebar();
} else {
toggleSidebar?.();
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
}
}

View File

@@ -10,14 +10,12 @@ import styled from "styled-components";
import { decodeTime } from "ulid";
import { Text } from "preact-i18n";
import { memo } from "preact/compat";
import { useEffect, useState } from "preact/hooks";
import { internalSubscribe, internalEmit } from "../../../lib/eventEmitter";
import { ChannelRenderer } from "../../../lib/renderer/Singleton";
import { connectState } from "../../../redux/connector";
import { QueuedMessage } from "../../../redux/reducers/queue";
import { useApplicationState } from "../../../mobx/State";
import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
import { useClient } from "../../../context/revoltjs/RevoltClient";
@@ -33,7 +31,6 @@ import MessageEditor from "./MessageEditor";
interface Props {
highlight?: string;
queue: QueuedMessage[];
renderer: ChannelRenderer;
}
@@ -48,9 +45,10 @@ const BlockedMessage = styled.div`
}
`;
const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
export default observer(({ renderer, highlight }: Props) => {
const client = useClient();
const userId = client.user!._id;
const queue = useApplicationState().queue;
const [editing, setEditing] = useState<string | undefined>(undefined);
const stopEditing = () => {
@@ -192,8 +190,7 @@ const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
const nonces = renderer.messages.map((x) => x.nonce);
if (renderer.atBottom) {
for (const msg of queue) {
if (msg.channel !== renderer.channel._id) continue;
for (const msg of queue.get(renderer.channel._id)) {
if (nonces.includes(msg.id)) continue;
if (previous) {
@@ -237,11 +234,3 @@ const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
return <>{render}</>;
});
export default memo(
connectState<Omit<Props, "queue">>(MessageRenderer, (state) => {
return {
queue: state.queue,
};
}),
);

View File

@@ -1,6 +1,6 @@
import { Wrench } from "@styled-icons/boxicons-solid";
import { useContext, useState } from "preact/hooks";
import { useContext, useEffect, useState } from "preact/hooks";
import PaintCounter from "../../lib/PaintCounter";
import { TextReact } from "../../lib/i18n";
@@ -16,10 +16,14 @@ export default function Developer() {
const userPermission = client.user!.permission;
const [ping, setPing] = useState<undefined | number>(client.websocket.ping);
setInterval(
() => setPing(client.websocket.ping),
client.options.heartbeat * 1e3,
);
useEffect(() => {
const timer = setInterval(
() => setPing(client.websocket.ping),
client.options.heartbeat * 1e3,
);
return () => clearInterval(timer);
}, []);
return (
<div>

View File

@@ -14,22 +14,20 @@ import styled, { css } from "styled-components";
import styles from "./Home.module.scss";
import "./snow.scss";
import { Text } from "preact-i18n";
import { useContext, useMemo, useState } from "preact/hooks";
import { useContext, useMemo } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { dispatch, getState } from "../../redux";
import { useApplicationState } from "../../mobx/State";
import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
import { AppContext } from "../../context/revoltjs/RevoltClient";
import wideSVG from "../../../public/assets/wide.svg";
import Emoji from "../../components/common/Emoji";
import Tooltip from "../../components/common/Tooltip";
import Header from "../../components/ui/Header";
import CategoryButton from "../../components/ui/fluent/CategoryButton";
const CHANNELS_SIDEBAR_KEY = "sidebar_channels";
const IconConainer = styled.div`
cursor: pointer;
color: var(--secondary-foreground);
@@ -57,29 +55,14 @@ const Overlay = styled.div`
export default function Home() {
const client = useContext(AppContext);
const [showChannels, setChannels] = useState(
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
);
const layout = useApplicationState().layout;
const toggleChannelSidebar = () => {
if (isTouchscreenDevice) {
return;
}
setChannels(!showChannels);
if (showChannels) {
dispatch({
type: "SECTION_TOGGLE_SET",
id: CHANNELS_SIDEBAR_KEY,
state: false,
});
} else {
dispatch({
type: "SECTION_TOGGLE_UNSET",
id: CHANNELS_SIDEBAR_KEY,
});
}
layout.toggleSectionState(SIDEBAR_CHANNELS, true);
};
const snowflakes = useMemo(() => {

View File

@@ -10,8 +10,6 @@ import { useContext, useEffect, useState } from "preact/hooks";
import { defer } from "../../lib/defer";
import { TextReact } from "../../lib/i18n";
import { dispatch } from "../../redux";
import RequiresOnline from "../../context/revoltjs/RequiresOnline";
import {
AppContext,
@@ -168,11 +166,9 @@ export default function Invite() {
defer(() => {
if (server) {
dispatch({
type: "UNREADS_MARK_MULTIPLE_READ",
channels:
server.channel_ids,
});
client.unreads!.markMultipleRead(
server.channel_ids,
);
history.push(
`/server/${server._id}/channel/${invite.channel_id}`,

View File

@@ -1,27 +1,27 @@
import { observer } from "mobx-react-lite";
import { Helmet } from "react-helmet";
import { Route, Switch } from "react-router-dom";
import { LIBRARY_VERSION } from "revolt.js";
import styles from "./Login.module.scss";
import { Text } from "preact-i18n";
import { useContext } from "preact/hooks";
import { ThemeContext } from "../../context/Theme";
import { AppContext } from "../../context/revoltjs/RevoltClient";
import { useApplicationState } from "../../mobx/State";
import LocaleSelector from "../../components/common/LocaleSelector";
import background from "./background.jpg";
import { Titlebar } from "../../components/native/Titlebar";
import { APP_VERSION } from "../../version";
import background from "./background.jpg";
import { FormCreate } from "./forms/FormCreate";
import { FormLogin } from "./forms/FormLogin";
import { FormReset, FormSendReset } from "./forms/FormReset";
import { FormResend, FormVerify } from "./forms/FormVerify";
export default function Login() {
const theme = useContext(ThemeContext);
const client = useContext(AppContext);
export default observer(() => {
const state = useApplicationState();
const theme = state.settings.theme;
const configuration = state.config.get();
return (
<>
@@ -30,13 +30,15 @@ export default function Login() {
)}
<div className={styles.login}>
<Helmet>
<meta name="theme-color" content={theme.background} />
<meta
name="theme-color"
content={theme.getVariable("background")}
/>
</Helmet>
<div className={styles.content}>
<div className={styles.attribution}>
<span>
API:{" "}
<code>{client.configuration?.revolt ?? "???"}</code>{" "}
API: <code>{configuration?.revolt ?? "???"}</code>{" "}
&middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "}
&middot; App: <code>{APP_VERSION}</code>
</span>
@@ -80,4 +82,4 @@ export default function Login() {
</div>
</>
);
}
});

View File

@@ -1,10 +1,11 @@
import HCaptcha from "@hcaptcha/react-hcaptcha";
import { observer } from "mobx-react-lite";
import styles from "../Login.module.scss";
import { Text } from "preact-i18n";
import { useContext, useEffect } from "preact/hooks";
import { useEffect } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { useApplicationState } from "../../../mobx/State";
import Preloader from "../../../components/ui/Preloader";
@@ -13,22 +14,22 @@ export interface CaptchaProps {
onCancel: () => void;
}
export function CaptchaBlock(props: CaptchaProps) {
const client = useContext(AppContext);
export const CaptchaBlock = observer((props: CaptchaProps) => {
const configuration = useApplicationState().config.get();
useEffect(() => {
if (!client.configuration?.features.captcha.enabled) {
if (!configuration?.features.captcha.enabled) {
props.onSuccess();
}
}, [client.configuration?.features.captcha.enabled, props]);
}, [configuration?.features.captcha.enabled, props]);
if (!client.configuration?.features.captcha.enabled)
if (!configuration?.features.captcha.enabled)
return <Preloader type="spinner" />;
return (
<div>
<HCaptcha
sitekey={client.configuration.features.captcha.key}
sitekey={configuration.features.captcha.key}
onVerify={(token) => props.onSuccess(token)}
/>
<div className={styles.footer}>
@@ -38,4 +39,4 @@ export function CaptchaBlock(props: CaptchaProps) {
</div>
</div>
);
}
});

View File

@@ -6,6 +6,8 @@ import styles from "../Login.module.scss";
import { Text } from "preact-i18n";
import { useContext, useState } from "preact/hooks";
import { useApplicationState } from "../../../mobx/State";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { takeError } from "../../../context/revoltjs/util";
@@ -44,7 +46,7 @@ interface FormInputs {
}
export function Form({ page, callback }: Props) {
const client = useContext(AppContext);
const configuration = useApplicationState().config.get();
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState<string | undefined>(undefined);
@@ -80,10 +82,7 @@ export function Form({ page, callback }: Props) {
}
try {
if (
client.configuration?.features.captcha.enabled &&
page !== "reset"
) {
if (configuration?.features.captcha.enabled && page !== "reset") {
setCaptcha({
onSuccess: async (captcha) => {
setCaptcha(undefined);
@@ -111,7 +110,7 @@ export function Form({ page, callback }: Props) {
if (typeof success !== "undefined") {
return (
<div className={styles.success}>
{client.configuration?.features.email ? (
{configuration?.features.email ? (
<>
<Envelope size={72} />
<h2>
@@ -172,15 +171,14 @@ export function Form({ page, callback }: Props) {
error={errors.password?.message}
/>
)}
{client.configuration?.features.invite_only &&
page === "create" && (
<FormField
type="invite"
register={register}
showOverline
error={errors.invite?.message}
/>
)}
{configuration?.features.invite_only && page === "create" && (
<FormField
type="invite"
register={register}
showOverline
error={errors.invite?.message}
/>
)}
{error && (
<Overline type="error" error={error}>
<Text id={`login.error.${page}`} />

View File

@@ -1,10 +1,9 @@
import { useContext } from "preact/hooks";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { useApplicationState } from "../../../mobx/State";
import { Form } from "./Form";
export function FormCreate() {
const client = useContext(AppContext);
const config = useApplicationState().config;
const client = config.createClient();
return <Form page="create" callback={(data) => client.register(data)} />;
}

View File

@@ -1,15 +1,16 @@
import { detect } from "detect-browser";
import { useHistory } from "react-router-dom";
import { Session } from "revolt-api/types/Auth";
import { Client } from "revolt.js";
import { useContext } from "preact/hooks";
import { useApplicationState } from "../../../mobx/State";
import { OperationsContext } from "../../../context/revoltjs/RevoltClient";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { Form } from "./Form";
export function FormLogin() {
const { login } = useContext(OperationsContext);
const history = useHistory();
const auth = useApplicationState().auth;
const { openScreen } = useIntermediate();
return (
<Form
@@ -34,8 +35,41 @@ export function FormLogin() {
friendly_name = "Unknown Device";
}
await login({ ...data, friendly_name });
history.push("/");
// ! FIXME: temporary login flow code
// This should be replaced in the future.
const client = new Client();
await client.fetchConfiguration();
const session = (await client.req(
"POST",
"/auth/session/login",
{ ...data, friendly_name },
)) as unknown as Session;
client.session = session;
(client as any).Axios.defaults.headers = {
"x-session-token": session?.token,
};
async function login() {
auth.setSession(session);
}
const { onboarding } = await client.req(
"GET",
"/onboard/hello",
);
if (onboarding) {
openScreen({
id: "onboarding",
callback: async (username: string) =>
client
.completeOnboarding({ username }, false)
.then(login),
});
} else {
login();
}
}}
/>
);

View File

@@ -2,12 +2,15 @@ import { useHistory, useParams } from "react-router-dom";
import { useContext } from "preact/hooks";
import { useApplicationState } from "../../../mobx/State";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { Form } from "./Form";
export function FormSendReset() {
const client = useContext(AppContext);
const config = useApplicationState().config;
const client = config.createClient();
return (
<Form

View File

@@ -2,6 +2,8 @@ import { useHistory, useParams } from "react-router-dom";
import { useContext, useEffect, useState } from "preact/hooks";
import { useApplicationState } from "../../../mobx/State";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { takeError } from "../../../context/revoltjs/util";
@@ -11,7 +13,8 @@ import Preloader from "../../../components/ui/Preloader";
import { Form } from "./Form";
export function FormResend() {
const client = useContext(AppContext);
const config = useApplicationState().config;
const client = config.createClient();
return (
<Form

View File

@@ -32,7 +32,6 @@ function mapMailProvider(email?: string): [string, string] | undefined {
case "outlook.com.br":
case "outlook.cl":
case "outlook.cz":
case "outlook.dk":
case "outlook.com.gr":
case "outlook.co.il":
case "outlook.in":

View File

@@ -15,7 +15,7 @@ import {
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { ThemeContext } from "../../context/Theme";
import { useApplicationState } from "../../mobx/State";
import Category from "../../components/ui/Category";
import Header from "../../components/ui/Header";
@@ -55,7 +55,7 @@ export function GenericSettings({
indexHeader,
}: Props) {
const history = useHistory();
const theme = useContext(ThemeContext);
const theme = useApplicationState().settings.theme;
const { page } = useParams<{ page: string }>();
const [closing, setClosing] = useState(false);
@@ -96,8 +96,8 @@ export function GenericSettings({
name="theme-color"
content={
isTouchscreenDevice
? theme["background"]
: theme["secondary-background"]
? theme.getVariable("background")
: theme.getVariable("secondary-background")
}
/>
</Helmet>

View File

@@ -18,6 +18,7 @@ import {
Store,
Bot,
} from "@styled-icons/boxicons-solid";
import { observer } from "mobx-react-lite";
import { Route, Switch, useHistory } from "react-router-dom";
import { LIBRARY_VERSION } from "revolt.js";
import styled from "styled-components";
@@ -26,13 +27,10 @@ import styles from "./Settings.module.scss";
import { Text } from "preact-i18n";
import { useContext } from "preact/hooks";
import { isExperimentEnabled } from "../../redux/reducers/experiments";
import { useApplicationState } from "../../mobx/State";
import RequiresOnline from "../../context/revoltjs/RequiresOnline";
import {
AppContext,
OperationsContext,
} from "../../context/revoltjs/RevoltClient";
import { AppContext, LogOutContext } from "../../context/revoltjs/RevoltClient";
import UserIcon from "../../components/common/user/UserIcon";
import LineDivider from "../../components/ui/LineDivider";
@@ -64,10 +62,11 @@ const IndexHeader = styled.div`
gap: 10px;
`;
export default function Settings() {
export default observer(() => {
const history = useHistory();
const client = useContext(AppContext);
const operations = useContext(OperationsContext);
const logout = useContext(LogOutContext);
const experiments = useApplicationState().experiments;
function switchPage(to?: string) {
if (to) {
@@ -138,14 +137,14 @@ export default function Settings() {
title: <Text id="app.settings.pages.experiments.title" />,
},
{
divider: !isExperimentEnabled("theme_shop"),
divider: !experiments.isEnabled("theme_shop"),
category: "revolt",
id: "bots",
icon: <Bot size={20} />,
title: <Text id="app.settings.pages.bots.title" />,
},
{
hidden: !isExperimentEnabled("theme_shop"),
hidden: !experiments.isEnabled("theme_shop"),
divider: true,
id: "theme_shop",
icon: <Store size={20} />,
@@ -191,7 +190,7 @@ export default function Settings() {
<Route path="/settings/bots">
<MyBots />
</Route>
{isExperimentEnabled("theme_shop") && (
{experiments.isEnabled("theme_shop") && (
<Route path="/settings/theme_shop">
<ThemeShop />
</Route>
@@ -229,7 +228,7 @@ export default function Settings() {
</a>
<LineDivider />
<ButtonItem
onClick={() => operations.logout()}
onClick={logout}
className={styles.logOut}
compact>
<LogOut size={20} />
@@ -277,4 +276,4 @@ export default function Settings() {
}
/>
);
}
});

View File

@@ -1,153 +0,0 @@
<svg width="323" height="202" viewBox="0 0 323 202" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="323" height="202" fill="#191919"/>
<path d="M27 14C27 11.7909 28.7909 10 31 10H90V202H31C28.7909 202 27 200.209 27 198V14Z" fill="#1E1E1E"/>
<rect x="90" y="10" width="233" height="192" fill="#242424"/>
<rect x="90" y="10" width="233" height="18" fill="#363636"/>
<rect x="97" y="16" width="30" height="6" rx="2" fill="#DEDEDE"/>
<path d="M106.517 163.445C111.534 163.445 115.601 159.378 115.601 154.361C115.601 149.344 111.534 145.277 106.517 145.277C101.5 145.277 97.4326 149.344 97.4326 154.361C97.4326 159.378 101.5 163.445 106.517 163.445Z" fill="#686868"/>
<path d="M150.206 145.277H124.252C123.296 145.277 122.522 146.052 122.522 147.008V150.468C122.522 151.424 123.296 152.198 124.252 152.198H150.206C151.162 152.198 151.936 151.424 151.936 150.468V147.008C151.936 146.052 151.162 145.277 150.206 145.277Z" fill="#E8E8E8"/>
<path d="M178.756 145.277H157.992C157.037 145.277 156.262 146.052 156.262 147.008V150.468C156.262 151.424 157.037 152.198 157.992 152.198H178.756C179.711 152.198 180.486 151.424 180.486 150.468V147.008C180.486 146.052 179.711 145.277 178.756 145.277Z" fill="#676767"/>
<path d="M141.555 158.255H124.252C123.296 158.255 122.522 159.029 122.522 159.985V161.715C122.522 162.671 123.296 163.445 124.252 163.445H141.555C142.51 163.445 143.285 162.671 143.285 161.715V159.985C143.285 159.029 142.51 158.255 141.555 158.255Z" fill="white"/>
<path d="M185.677 158.255H148.476C147.52 158.255 146.746 159.029 146.746 159.985V161.715C146.746 162.671 147.52 163.445 148.476 163.445H185.677C186.632 163.445 187.407 162.671 187.407 161.715V159.985C187.407 159.029 186.632 158.255 185.677 158.255Z" fill="white"/>
<path d="M236.72 158.255H192.598C191.642 158.255 190.868 159.029 190.868 159.985V161.715C190.868 162.671 191.642 163.445 192.598 163.445H236.72C237.676 163.445 238.45 162.671 238.45 161.715V159.985C238.45 159.029 237.676 158.255 236.72 158.255Z" fill="white"/>
<path opacity="0.5" d="M97 131.868H262.242" stroke="#707070" stroke-width="0.86514" stroke-linecap="round"/>
<path d="M150.206 81.257H124.252C123.296 81.257 122.522 82.0316 122.522 82.9872V86.4478C122.522 87.4034 123.296 88.1781 124.252 88.1781H150.206C151.162 88.1781 151.936 87.4034 151.936 86.4478V82.9872C151.936 82.0316 151.162 81.257 150.206 81.257Z" fill="#E8E8E8"/>
<path d="M178.756 81.257H157.992C157.037 81.257 156.262 82.0316 156.262 82.9872V86.4478C156.262 87.4034 157.037 88.1781 157.992 88.1781H178.756C179.711 88.1781 180.486 87.4034 180.486 86.4478V82.9872C180.486 82.0316 179.711 81.257 178.756 81.257Z" fill="#676767"/>
<path d="M190.868 94.2341H124.252C123.296 94.2341 122.522 95.0088 122.522 95.9644V97.6947C122.522 98.6503 123.296 99.425 124.252 99.425H190.868C191.823 99.425 192.598 98.6503 192.598 97.6947V95.9644C192.598 95.0088 191.823 94.2341 190.868 94.2341Z" fill="#68ABEE"/>
<path d="M146.746 106.708H130.308C129.352 106.708 128.578 107.483 128.578 108.439V110.169C128.578 111.124 129.352 111.899 130.308 111.899H146.746C147.701 111.899 148.476 111.124 148.476 110.169V108.439C148.476 107.483 147.701 106.708 146.746 106.708Z" fill="#68ABEE"/>
<path d="M215.957 114.997H130.308C129.352 114.997 128.578 115.772 128.578 116.728V118.458C128.578 119.413 129.352 120.188 130.308 120.188H215.957C216.912 120.188 217.687 119.413 217.687 118.458V116.728C217.687 115.772 216.912 114.997 215.957 114.997Z" fill="#888888"/>
<path d="M122.954 105.913V120.621" stroke="#707070" stroke-width="0.86514" stroke-linecap="round"/>
<path d="M106.517 99.4249C111.534 99.4249 115.601 95.3579 115.601 90.3409C115.601 85.324 111.534 81.257 106.517 81.257C101.5 81.257 97.4326 85.324 97.4326 90.3409C97.4326 95.3579 101.5 99.4249 106.517 99.4249Z" fill="#686868"/>
<path d="M106.517 56.1679C111.534 56.1679 115.601 52.1009 115.601 47.084C115.601 42.067 111.534 38 106.517 38C101.5 38 97.4326 42.067 97.4326 47.084C97.4326 52.1009 101.5 56.1679 106.517 56.1679Z" fill="#686868"/>
<path d="M150.206 38H124.252C123.296 38 122.522 38.7747 122.522 39.7303V43.1908C122.522 44.1464 123.296 44.9211 124.252 44.9211H150.206C151.162 44.9211 151.936 44.1464 151.936 43.1908V39.7303C151.936 38.7747 151.162 38 150.206 38Z" fill="#E8E8E8"/>
<path d="M178.756 38H157.992C157.037 38 156.262 38.7747 156.262 39.7303V43.1908C156.262 44.1464 157.037 44.9211 157.992 44.9211H178.756C179.711 44.9211 180.486 44.1464 180.486 43.1908V39.7303C180.486 38.7747 179.711 38 178.756 38Z" fill="#676767"/>
<path d="M202.98 50.9771H124.252C123.296 50.9771 122.522 51.7517 122.522 52.7073V54.4376C122.522 55.3932 123.296 56.1679 124.252 56.1679H202.98C203.935 56.1679 204.71 55.3932 204.71 54.4376V52.7073C204.71 51.7517 203.935 50.9771 202.98 50.9771Z" fill="white"/>
<path d="M145.88 62.2239H124.252C123.296 62.2239 122.522 62.9985 122.522 63.9542V65.6844C122.522 66.64 123.296 67.4147 124.252 67.4147H145.88C146.836 67.4147 147.611 66.64 147.611 65.6844V63.9542C147.611 62.9985 146.836 62.2239 145.88 62.2239Z" fill="white"/>
<path d="M192.598 62.2239H152.802C151.846 62.2239 151.071 62.9985 151.071 63.9542V65.6844C151.071 66.64 151.846 67.4147 152.802 67.4147H192.598C193.554 67.4147 194.328 66.64 194.328 65.6844V63.9542C194.328 62.9985 193.554 62.2239 192.598 62.2239Z" fill="white"/>
<path d="M265.27 50.9771H209.901C208.945 50.9771 208.17 51.7517 208.17 52.7073V54.4376C208.17 55.3932 208.945 56.1679 209.901 56.1679H265.27C266.225 56.1679 267 55.3932 267 54.4376V52.7073C267 51.7517 266.225 50.9771 265.27 50.9771Z" fill="white"/>
<rect x="90" y="184" width="233" height="18" fill="#363636"/>
<circle cx="317" cy="5" r="2" fill="#C4C4C4"/>
<circle cx="310" cy="5" r="2" fill="#C4C4C4"/>
<circle cx="303" cy="5" r="2" fill="#C4C4C4"/>
<line x1="4.5" y1="34.5" x2="21.5" y2="34.5" stroke="#414141" stroke-linecap="round"/>
<rect x="30" y="16" width="36" height="6" rx="2" fill="#F3F3F3"/>
<rect x="30" y="35" width="26" height="4" rx="2" fill="#F3F3F3"/>
<rect x="39" y="46" width="32" height="4" rx="2" fill="#BBBBBB"/>
<rect x="39" y="70" width="29" height="4" rx="2" fill="#BBBBBB"/>
<rect x="39" y="58" width="13" height="4" rx="2" fill="#BBBBBB"/>
<rect x="55" y="58" width="22" height="4" rx="2" fill="#BBBBBB"/>
<rect x="30" y="83" width="26" height="4" rx="2" fill="#F3F3F3"/>
<rect x="39" y="94" width="32" height="4" rx="2" fill="#BBBBBB"/>
<rect x="39" y="118" width="29" height="4" rx="2" fill="#BBBBBB"/>
<rect x="39" y="106" width="13" height="4" rx="2" fill="#BBBBBB"/>
<rect x="55" y="106" width="22" height="4" rx="2" fill="#BBBBBB"/>
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="10" width="18" height="18">
<circle cx="13" cy="19" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask0)">
<circle cx="13" cy="19" r="9" fill="url(#paint0_linear)"/>
<circle cx="11.92" cy="22.24" r="3.6" fill="#F9FAFB"/>
<path d="M4 22.6H6.88L9.04 21.52L11.2 20.8L12.64 21.52L14.44 21.16L16.24 21.52L16.96 21.88L19.12 21.52L20.56 21.88L22 21.16V29.08H16.6H11.92H4V24.04V22.6Z" fill="#C42626"/>
<path d="M6.88 22.6H4V24.04L6.88 22.6Z" fill="#882C2F"/>
<path d="M14.44 21.16L12.64 21.52L11.2 24.04L11.92 29.08H16.6L15.88 27.64L16.24 22.96L16.96 21.88L16.24 21.52L14.44 21.16Z" fill="#AF373B"/>
</g>
<mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="42" width="18" height="18">
<circle cx="13" cy="51" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask1)">
<circle cx="13" cy="51" r="9" fill="#D6D4D5"/>
<path d="M13.612 53.8162C19.048 49.6124 22.0252 53.8162 22.0252 53.8162V60.4402H5.89721L7.49201 53.988C7.99666 53.5705 8.17601 58.02 13.612 53.8162Z" fill="url(#paint1_linear)"/>
<path d="M4.53998 54.0601C4.53998 54.0601 8.10867 49.2615 12.388 54.9961C16.3011 60.2399 20.3145 56.741 20.668 55.8518V55.6801C20.7021 55.7083 20.7011 55.7686 20.668 55.8518V60.684H4.53998V54.0601Z" fill="url(#paint2_linear)"/>
<path d="M21.568 47.1119C21.568 48.2254 20.6654 49.1279 19.552 49.1279C18.4386 49.1279 17.536 48.2254 17.536 47.1119C17.536 45.9985 18.4386 45.0959 19.552 45.0959C20.6654 45.0959 21.568 45.9985 21.568 47.1119Z" fill="#E76563"/>
<path d="M19.12 49.0559H19.984V49.4879H19.12V49.0559Z" fill="#E76563"/>
<rect x="19.12" y="49.344" width="0.864" height="0.072" fill="white"/>
<path d="M19.264 49.488H19.336V49.776H19.264V49.488Z" fill="#4F65B6"/>
<path d="M19.48 49.488H19.624V49.776H19.48V49.488Z" fill="#4F65B6"/>
<path d="M19.768 49.488H19.84V49.776H19.768V49.488Z" fill="#4F65B6"/>
<path d="M19.048 49.776H20.056L19.984 50.28H19.12L19.048 49.776Z" fill="#4F65B6"/>
</g>
<circle cx="20" cy="45" r="3.5" fill="#EF3B3B" stroke="black"/>
<mask id="mask2" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="65" width="18" height="18">
<circle cx="13" cy="74" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask2)">
<circle cx="13" cy="74" r="9" fill="url(#paint3_linear)"/>
<path d="M11.056 79.184L13.936 75.764V80.516L11.992 80.336L11.056 79.184Z" fill="#2E2816"/>
<path d="M5.97998 76.2679L13.72 80.3719L13.792 85.0159L5.97998 82.3519L5.15198 79.1839L5.97998 76.2679Z" fill="url(#paint4_linear)"/>
<path d="M4.75598 78.0319L5.97998 76.2679L5.22398 79.4719L4.93598 78.5359L4.75598 78.0319Z" fill="#7EA6A6"/>
<path d="M19.12 68.708L21.64 70.544L24.484 76.124L22.468 79.94L19.12 68.708Z" fill="#EDEDED"/>
<path d="M12.964 79.976L13.864 80.444L13.936 84.008L13 83.972L12.964 79.976Z" fill="#878787" fill-opacity="0.5"/>
<path d="M13.468 75.584L19.12 68.708L23.008 79.112L13.72 85.736L13.468 75.584Z" fill="url(#paint5_linear)"/>
</g>
<mask id="mask3" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="88" width="18" height="18">
<circle cx="13" cy="97" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask3)">
<circle cx="13" cy="97" r="9" fill="url(#paint6_linear)"/>
<path d="M4.252 89.404C4.252 89.404 11.236 87.2439 16.024 92.284C20.812 97.324 19.732 106.396 19.732 106.396H4.252L3.604 97.936L4.252 89.404Z" fill="url(#paint7_linear)"/>
<path d="M14.404 106.396C12.208 111.508 19.732 106.396 19.732 106.396C19.732 106.396 20.488 100.348 18.508 95.956C16.528 91.564 13.72 90.448 13.72 90.448C13.72 90.448 16.6 101.284 14.404 106.396Z" fill="url(#paint8_linear)"/>
</g>
<mask id="mask4" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="110" width="18" height="18">
<circle cx="13" cy="119" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask4)">
<circle cx="13" cy="119" r="9" fill="url(#paint9_linear)"/>
<path d="M3.35199 122.708L22.216 122.708" stroke="#8181B1" stroke-width="0.144"/>
<path d="M3.28003 121.376L22.144 121.376" stroke="#A2A2BE" stroke-width="0.144"/>
<path d="M3.56799 119.936L22.432 119.936" stroke="#ADADBD" stroke-width="0.216"/>
<path d="M3.784 118.496L22.648 118.496" stroke="#BBBBCD" stroke-width="0.216"/>
<line x1="3.35199" y1="124.004" x2="22.216" y2="124.004" stroke="#8181B1" stroke-width="0.072"/>
<line x1="3.35199" y1="125.3" x2="22.216" y2="125.3" stroke="#8181B1" stroke-width="0.072"/>
<path d="M13.144 122.816L13.828 123.824C13.828 123.824 13.936 124.256 13.828 124.328C13.72 124.4 13.288 123.824 13.18 123.5C13.072 123.176 13.144 122.816 13.144 122.816Z" fill="#E6E7F4"/>
<path d="M13.828 124.328V123.824C13.828 123.824 15.304 123.608 16.708 122.816C18.112 122.024 18.364 120.944 18.364 120.944C18.364 120.944 18.292 121.952 16.924 123.032C15.556 124.112 13.828 124.328 13.828 124.328Z" fill="#E6E7F4"/>
<path d="M18.364 120.944C15.448 120.764 13.144 122.826 13.144 122.826L13.828 123.834C13.828 123.834 17.644 123.248 18.364 120.944Z" fill="white"/>
<path d="M18.256 121.016C15.6819 120.86 13.252 122.816 13.252 122.816L13.864 123.716C13.864 123.716 17.6204 123.017 18.256 121.016Z" fill="url(#paint10_linear)"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="13" y1="10" x2="13" y2="28" gradientUnits="userSpaceOnUse">
<stop stop-color="#AAB6BD"/>
<stop offset="1" stop-color="#D4DDE1"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="14.908" y1="54.744" x2="22.756" y2="61.08" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F65B6"/>
<stop offset="1" stop-color="#C6D0F1"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="8.39198" y1="54.276" x2="21.496" y2="60.324" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F65B6"/>
<stop offset="1" stop-color="#C6D0F1"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="9.292" y1="65.432" x2="18.22" y2="82.388" gradientUnits="userSpaceOnUse">
<stop stop-color="#009092"/>
<stop offset="1" stop-color="#79C6C8"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="9.39998" y1="76.2679" x2="9.39998" y2="85.0519" gradientUnits="userSpaceOnUse">
<stop stop-color="#CBCBCB"/>
<stop offset="1" stop-color="#FAFAFA"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="18.238" y1="68.708" x2="18.238" y2="85.736" gradientUnits="userSpaceOnUse">
<stop stop-color="#95ABA9"/>
<stop offset="1" stop-color="#DCDCDC"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="10.876" y1="87.604" x2="17.86" y2="105.136" gradientUnits="userSpaceOnUse">
<stop stop-color="#41486A"/>
<stop offset="1" stop-color="#3B3F5C"/>
</linearGradient>
<linearGradient id="paint7_linear" x1="7.312" y1="91.168" x2="19.84" y2="107.224" gradientUnits="userSpaceOnUse">
<stop stop-color="#4C7799"/>
<stop offset="0.9999" stop-color="#39AEBF"/>
<stop offset="1" stop-color="#4C7799" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint8_linear" x1="16.636" y1="96.568" x2="12.388" y2="87.316" gradientUnits="userSpaceOnUse">
<stop stop-color="#DD4878"/>
<stop offset="1" stop-color="#D7E1E8"/>
</linearGradient>
<linearGradient id="paint9_linear" x1="9.94" y1="109.244" x2="13.756" y2="125.912" gradientUnits="userSpaceOnUse">
<stop stop-color="#847DAF"/>
<stop offset="1" stop-color="#4547AE"/>
</linearGradient>
<linearGradient id="paint10_linear" x1="15.484" y1="122.024" x2="16.924" y2="123.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#DFDFE1"/>
<stop offset="1" stop-color="#F5F4FB"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,153 +0,0 @@
<svg width="323" height="202" viewBox="0 0 323 202" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="323" height="202" fill="#F6F6F6"/>
<path d="M27 14C27 11.7909 28.7909 10 31 10H90V202H31C28.7909 202 27 200.209 27 198V14Z" fill="#EDEDED"/>
<rect x="90" y="10" width="233" height="192" fill="white"/>
<rect x="90" y="10" width="233" height="18" fill="#E8E8E8"/>
<rect x="97" y="16" width="30" height="6" rx="2" fill="#C4C4C4"/>
<path d="M106.517 163.445C111.534 163.445 115.601 159.378 115.601 154.361C115.601 149.344 111.534 145.277 106.517 145.277C101.5 145.277 97.4326 149.344 97.4326 154.361C97.4326 159.378 101.5 163.445 106.517 163.445Z" fill="#CFCFCF"/>
<path d="M150.206 145.277H124.252C123.296 145.277 122.522 146.052 122.522 147.008V150.468C122.522 151.424 123.296 152.198 124.252 152.198H150.206C151.162 152.198 151.936 151.424 151.936 150.468V147.008C151.936 146.052 151.162 145.277 150.206 145.277Z" fill="#464646"/>
<path d="M178.756 145.277H157.992C157.037 145.277 156.262 146.052 156.262 147.008V150.468C156.262 151.424 157.037 152.198 157.992 152.198H178.756C179.711 152.198 180.486 151.424 180.486 150.468V147.008C180.486 146.052 179.711 145.277 178.756 145.277Z" fill="#676767"/>
<path d="M141.555 158.255H124.252C123.296 158.255 122.522 159.029 122.522 159.985V161.715C122.522 162.671 123.296 163.445 124.252 163.445H141.555C142.51 163.445 143.285 162.671 143.285 161.715V159.985C143.285 159.029 142.51 158.255 141.555 158.255Z" fill="#4A4A4A"/>
<path d="M185.677 158.255H148.476C147.52 158.255 146.746 159.029 146.746 159.985V161.715C146.746 162.671 147.52 163.445 148.476 163.445H185.677C186.633 163.445 187.407 162.671 187.407 161.715V159.985C187.407 159.029 186.633 158.255 185.677 158.255Z" fill="#4A4A4A"/>
<path d="M236.72 158.255H192.598C191.642 158.255 190.868 159.029 190.868 159.985V161.715C190.868 162.671 191.642 163.445 192.598 163.445H236.72C237.676 163.445 238.45 162.671 238.45 161.715V159.985C238.45 159.029 237.676 158.255 236.72 158.255Z" fill="#4A4A4A"/>
<path opacity="0.5" d="M97 131.868H262.242" stroke="#707070" stroke-width="0.86514" stroke-linecap="round"/>
<path d="M150.206 81.257H124.252C123.296 81.257 122.522 82.0316 122.522 82.9872V86.4478C122.522 87.4034 123.296 88.1781 124.252 88.1781H150.206C151.162 88.1781 151.936 87.4034 151.936 86.4478V82.9872C151.936 82.0316 151.162 81.257 150.206 81.257Z" fill="#464646"/>
<path d="M178.756 81.257H157.992C157.037 81.257 156.262 82.0316 156.262 82.9872V86.4478C156.262 87.4034 157.037 88.1781 157.992 88.1781H178.756C179.711 88.1781 180.486 87.4034 180.486 86.4478V82.9872C180.486 82.0316 179.711 81.257 178.756 81.257Z" fill="#676767"/>
<path d="M190.868 94.2341H124.252C123.296 94.2341 122.522 95.0088 122.522 95.9644V97.6947C122.522 98.6503 123.296 99.425 124.252 99.425H190.868C191.823 99.425 192.598 98.6503 192.598 97.6947V95.9644C192.598 95.0088 191.823 94.2341 190.868 94.2341Z" fill="#68ABEE"/>
<path d="M146.746 106.708H130.308C129.352 106.708 128.578 107.483 128.578 108.439V110.169C128.578 111.124 129.352 111.899 130.308 111.899H146.746C147.701 111.899 148.476 111.124 148.476 110.169V108.439C148.476 107.483 147.701 106.708 146.746 106.708Z" fill="#68ABEE"/>
<path d="M215.957 114.997H130.308C129.352 114.997 128.578 115.772 128.578 116.728V118.458C128.578 119.413 129.352 120.188 130.308 120.188H215.957C216.912 120.188 217.687 119.413 217.687 118.458V116.728C217.687 115.772 216.912 114.997 215.957 114.997Z" fill="#4A4A4A"/>
<path d="M122.954 105.913V120.621" stroke="#BFBFBF" stroke-width="0.86514" stroke-linecap="round"/>
<path d="M106.517 99.4249C111.534 99.4249 115.601 95.3579 115.601 90.3409C115.601 85.324 111.534 81.257 106.517 81.257C101.5 81.257 97.4326 85.324 97.4326 90.3409C97.4326 95.3579 101.5 99.4249 106.517 99.4249Z" fill="#CFCFCF"/>
<path d="M106.517 56.1679C111.534 56.1679 115.601 52.1009 115.601 47.084C115.601 42.067 111.534 38 106.517 38C101.5 38 97.4326 42.067 97.4326 47.084C97.4326 52.1009 101.5 56.1679 106.517 56.1679Z" fill="#CFCFCF"/>
<path d="M150.206 38H124.252C123.296 38 122.522 38.7747 122.522 39.7303V43.1908C122.522 44.1464 123.296 44.9211 124.252 44.9211H150.206C151.162 44.9211 151.936 44.1464 151.936 43.1908V39.7303C151.936 38.7747 151.162 38 150.206 38Z" fill="#464646"/>
<path d="M178.756 38H157.992C157.037 38 156.262 38.7747 156.262 39.7303V43.1908C156.262 44.1464 157.037 44.9211 157.992 44.9211H178.756C179.711 44.9211 180.486 44.1464 180.486 43.1908V39.7303C180.486 38.7747 179.711 38 178.756 38Z" fill="#676767"/>
<path d="M202.98 50.9771H124.252C123.296 50.9771 122.522 51.7517 122.522 52.7073V54.4376C122.522 55.3932 123.296 56.1679 124.252 56.1679H202.98C203.935 56.1679 204.71 55.3932 204.71 54.4376V52.7073C204.71 51.7517 203.935 50.9771 202.98 50.9771Z" fill="#4A4A4A"/>
<path d="M145.88 62.2239H124.252C123.296 62.2239 122.522 62.9985 122.522 63.9542V65.6844C122.522 66.64 123.296 67.4147 124.252 67.4147H145.88C146.836 67.4147 147.611 66.64 147.611 65.6844V63.9542C147.611 62.9985 146.836 62.2239 145.88 62.2239Z" fill="#4A4A4A"/>
<path d="M192.598 62.2239H152.802C151.846 62.2239 151.071 62.9985 151.071 63.9542V65.6844C151.071 66.64 151.846 67.4147 152.802 67.4147H192.598C193.554 67.4147 194.328 66.64 194.328 65.6844V63.9542C194.328 62.9985 193.554 62.2239 192.598 62.2239Z" fill="#4A4A4A"/>
<path d="M265.27 50.9771H209.901C208.945 50.9771 208.17 51.7517 208.17 52.7073V54.4376C208.17 55.3932 208.945 56.1679 209.901 56.1679H265.27C266.225 56.1679 267 55.3932 267 54.4376V52.7073C267 51.7517 266.225 50.9771 265.27 50.9771Z" fill="#4A4A4A"/>
<rect x="90" y="184" width="233" height="18" fill="#D3D3D3"/>
<circle cx="317" cy="5" r="2" fill="#C4C4C4"/>
<circle cx="310" cy="5" r="2" fill="#C4C4C4"/>
<circle cx="303" cy="5" r="2" fill="#C4C4C4"/>
<line x1="4.5" y1="34.5" x2="21.5" y2="34.5" stroke="#C0C0C0" stroke-linecap="round"/>
<rect x="30" y="16" width="36" height="6" rx="2" fill="#BCBCBC"/>
<rect x="30" y="35" width="26" height="4" rx="2" fill="#676565"/>
<rect x="39" y="46" width="32" height="4" rx="2" fill="#9A9A9A"/>
<rect x="39" y="70" width="29" height="4" rx="2" fill="#9A9A9A"/>
<rect x="39" y="58" width="13" height="4" rx="2" fill="#9A9A9A"/>
<rect x="55" y="58" width="22" height="4" rx="2" fill="#9A9A9A"/>
<rect x="30" y="83" width="26" height="4" rx="2" fill="#676565"/>
<rect x="39" y="94" width="32" height="4" rx="2" fill="#9A9A9A"/>
<rect x="39" y="118" width="29" height="4" rx="2" fill="#9A9A9A"/>
<rect x="39" y="106" width="13" height="4" rx="2" fill="#9A9A9A"/>
<rect x="55" y="106" width="22" height="4" rx="2" fill="#9A9A9A"/>
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="10" width="18" height="18">
<circle cx="13" cy="19" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask0)">
<circle cx="13" cy="19" r="9" fill="url(#paint0_linear)"/>
<circle cx="11.9199" cy="22.24" r="3.6" fill="#F9FAFB"/>
<path d="M4 22.6H6.88L9.04 21.52L11.2 20.8L12.64 21.52L14.44 21.16L16.24 21.52L16.96 21.88L19.12 21.52L20.56 21.88L22 21.16V29.08H16.6H11.92H4V24.04V22.6Z" fill="#C42626"/>
<path d="M6.88 22.6H4V24.04L6.88 22.6Z" fill="#882C2F"/>
<path d="M14.44 21.16L12.64 21.52L11.2 24.04L11.92 29.08H16.6L15.88 27.64L16.24 22.96L16.96 21.88L16.24 21.52L14.44 21.16Z" fill="#AF373B"/>
</g>
<mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="42" width="18" height="18">
<circle cx="13" cy="51" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask1)">
<circle cx="13" cy="51" r="9" fill="#D6D4D5"/>
<path d="M13.612 53.8162C19.048 49.6124 22.0251 53.8162 22.0251 53.8162V60.4402H5.89715L7.49195 53.988C7.9966 53.5705 8.17595 58.02 13.612 53.8162Z" fill="url(#paint1_linear)"/>
<path d="M4.54004 54.0601C4.54004 54.0601 8.10873 49.2615 12.388 54.9961C16.3012 60.2399 20.3146 56.741 20.668 55.8518V55.6801C20.7021 55.7083 20.7011 55.7686 20.668 55.8518V60.684H4.54004V54.0601Z" fill="url(#paint2_linear)"/>
<path d="M21.568 47.1119C21.568 48.2254 20.6654 49.1279 19.552 49.1279C18.4386 49.1279 17.536 48.2254 17.536 47.1119C17.536 45.9985 18.4386 45.0959 19.552 45.0959C20.6654 45.0959 21.568 45.9985 21.568 47.1119Z" fill="#E76563"/>
<path d="M19.12 49.0559H19.984V49.4879H19.12V49.0559Z" fill="#E76563"/>
<rect x="19.12" y="49.344" width="0.864" height="0.072" fill="white"/>
<path d="M19.264 49.488H19.336V49.776H19.264V49.488Z" fill="#4F65B6"/>
<path d="M19.48 49.488H19.624V49.776H19.48V49.488Z" fill="#4F65B6"/>
<path d="M19.768 49.488H19.84V49.776H19.768V49.488Z" fill="#4F65B6"/>
<path d="M19.048 49.776H20.056L19.984 50.28H19.12L19.048 49.776Z" fill="#4F65B6"/>
</g>
<mask id="mask2" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="65" width="18" height="18">
<circle cx="13" cy="74" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask2)">
<circle cx="13" cy="74" r="9" fill="url(#paint3_linear)"/>
<path d="M11.056 79.184L13.936 75.764V80.516L11.992 80.336L11.056 79.184Z" fill="#2E2816"/>
<path d="M5.97998 76.2679L13.72 80.3719L13.792 85.0159L5.97998 82.3519L5.15198 79.1839L5.97998 76.2679Z" fill="url(#paint4_linear)"/>
<path d="M4.75598 78.0319L5.97998 76.2679L5.22398 79.4719L4.93598 78.5359L4.75598 78.0319Z" fill="#7EA6A6"/>
<path d="M19.12 68.708L21.64 70.544L24.484 76.124L22.468 79.94L19.12 68.708Z" fill="#EDEDED"/>
<path d="M12.964 79.976L13.864 80.444L13.936 84.008L13 83.972L12.964 79.976Z" fill="#878787" fill-opacity="0.5"/>
<path d="M13.468 75.584L19.12 68.708L23.008 79.112L13.72 85.736L13.468 75.584Z" fill="url(#paint5_linear)"/>
</g>
<mask id="mask3" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="88" width="18" height="18">
<circle cx="13" cy="97" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask3)">
<circle cx="13" cy="97" r="9" fill="url(#paint6_linear)"/>
<path d="M4.252 89.404C4.252 89.404 11.236 87.2439 16.024 92.284C20.812 97.324 19.732 106.396 19.732 106.396H4.252L3.604 97.936L4.252 89.404Z" fill="url(#paint7_linear)"/>
<path d="M14.404 106.396C12.208 111.508 19.732 106.396 19.732 106.396C19.732 106.396 20.488 100.348 18.508 95.956C16.528 91.564 13.72 90.448 13.72 90.448C13.72 90.448 16.6 101.284 14.404 106.396Z" fill="url(#paint8_linear)"/>
</g>
<mask id="mask4" mask-type="alpha" maskUnits="userSpaceOnUse" x="4" y="110" width="18" height="18">
<circle cx="13" cy="119" r="9" fill="#C4C4C4"/>
</mask>
<g mask="url(#mask4)">
<circle cx="13" cy="119" r="9" fill="url(#paint9_linear)"/>
<path d="M3.35205 122.708L22.2161 122.708" stroke="#8181B1" stroke-width="0.144"/>
<path d="M3.28003 121.376L22.144 121.376" stroke="#A2A2BE" stroke-width="0.144"/>
<path d="M3.56799 119.936L22.432 119.936" stroke="#ADADBD" stroke-width="0.216"/>
<path d="M3.78406 118.496L22.6481 118.496" stroke="#BBBBCD" stroke-width="0.216"/>
<line x1="3.35205" y1="124.004" x2="22.2161" y2="124.004" stroke="#8181B1" stroke-width="0.072"/>
<line x1="3.35205" y1="125.3" x2="22.2161" y2="125.3" stroke="#8181B1" stroke-width="0.072"/>
<path d="M13.144 122.816L13.828 123.824C13.828 123.824 13.936 124.256 13.828 124.328C13.72 124.4 13.288 123.824 13.18 123.5C13.072 123.176 13.144 122.816 13.144 122.816Z" fill="#E6E7F4"/>
<path d="M13.828 124.328V123.824C13.828 123.824 15.304 123.608 16.708 122.816C18.112 122.024 18.364 120.944 18.364 120.944C18.364 120.944 18.292 121.952 16.924 123.032C15.556 124.112 13.828 124.328 13.828 124.328Z" fill="#E6E7F4"/>
<path d="M18.364 120.944C15.448 120.764 13.144 122.826 13.144 122.826L13.828 123.834C13.828 123.834 17.644 123.248 18.364 120.944Z" fill="white"/>
<path d="M18.256 121.016C15.6818 120.86 13.252 122.816 13.252 122.816L13.864 123.716C13.864 123.716 17.6204 123.017 18.256 121.016Z" fill="url(#paint10_linear)"/>
</g>
<circle cx="20" cy="45" r="3.5" fill="#EF3B3B" stroke="#F6F6F6"/>
<defs>
<linearGradient id="paint0_linear" x1="13" y1="10" x2="13" y2="28" gradientUnits="userSpaceOnUse">
<stop stop-color="#AAB6BD"/>
<stop offset="1" stop-color="#D4DDE1"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="14.908" y1="54.744" x2="22.7559" y2="61.08" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F65B6"/>
<stop offset="1" stop-color="#C6D0F1"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="8.39204" y1="54.276" x2="21.496" y2="60.324" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F65B6"/>
<stop offset="1" stop-color="#C6D0F1"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="9.292" y1="65.432" x2="18.22" y2="82.388" gradientUnits="userSpaceOnUse">
<stop stop-color="#009092"/>
<stop offset="1" stop-color="#79C6C8"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="9.39998" y1="76.2679" x2="9.39998" y2="85.0519" gradientUnits="userSpaceOnUse">
<stop stop-color="#CBCBCB"/>
<stop offset="1" stop-color="#FAFAFA"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="18.238" y1="68.708" x2="18.238" y2="85.736" gradientUnits="userSpaceOnUse">
<stop stop-color="#95ABA9"/>
<stop offset="1" stop-color="#DCDCDC"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="10.876" y1="87.604" x2="17.86" y2="105.136" gradientUnits="userSpaceOnUse">
<stop stop-color="#41486A"/>
<stop offset="1" stop-color="#3B3F5C"/>
</linearGradient>
<linearGradient id="paint7_linear" x1="7.312" y1="91.168" x2="19.84" y2="107.224" gradientUnits="userSpaceOnUse">
<stop stop-color="#4C7799"/>
<stop offset="0.9999" stop-color="#39AEBF"/>
<stop offset="1" stop-color="#4C7799" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint8_linear" x1="16.636" y1="96.568" x2="12.388" y2="87.316" gradientUnits="userSpaceOnUse">
<stop stop-color="#DD4878"/>
<stop offset="1" stop-color="#D7E1E8"/>
</linearGradient>
<linearGradient id="paint9_linear" x1="9.94" y1="109.244" x2="13.756" y2="125.912" gradientUnits="userSpaceOnUse">
<stop stop-color="#847DAF"/>
<stop offset="1" stop-color="#4547AE"/>
</linearGradient>
<linearGradient id="paint10_linear" x1="15.484" y1="122.024" x2="16.924" y2="123.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#DFDFE1"/>
<stop offset="1" stop-color="#F5F4FB"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="168" height="48" fill="none" xmlns:v="https://vecta.io/nano"><style><![CDATA[.B{fill-rule:evenodd}.C{stroke-linejoin:round}.D{stroke-width:6}.E{fill:#ffc20b}.F{stroke-miterlimit:1.5}.G{stroke-linecap:square}]]></style><path d="M46.654 31.489c0 3.014-.788 5.153-2.017 6.712-1.241 1.574-3.025 2.676-5.214 3.436-4.442 1.542-10.158 1.557-15.274 1.557s-10.909-.014-15.43-1.559c-2.23-.762-4.052-1.867-5.319-3.444-1.253-1.559-2.056-3.695-2.056-6.702 0-5.622 1.211-12.465 4.663-17.856 3.4-5.311 9.019-9.299 18.142-9.299 8.647 0 14.204 3.995 17.657 9.347 3.497 5.42 4.848 12.27 4.848 17.808z" fill="#fbc546" stroke="#000" stroke-width="2.69"/><g class="B"><path d="M16.273 33.318c-2.399-1.845-4.126-4.52-4.75-7.593h25.08c-.624 3.073-2.351 5.747-4.75 7.593-1.423-1.542-14.156-1.542-15.579 0z" fill="#722245"/><path d="M16.274 33.318a7.68 7.68 0 0 1 5.646-2.474h4.287a7.68 7.68 0 0 1 5.646 2.474 12.74 12.74 0 0 1-7.79 2.645 12.74 12.74 0 0 1-7.79-2.645z" fill="#ca3b8f"/><path d="M15.747 24.446c1.188 0 2.325-.472 3.165-1.312s1.312-1.977 1.312-3.165v-2.562a4.48 4.48 0 0 0-4.479-4.479h-.003c-1.188 0-2.325.472-3.165 1.312s-1.312 1.977-1.312 3.165v2.562a4.48 4.48 0 0 0 4.479 4.479h.003 0z" fill="#fff"/><path d="M13.825 16.768a2.56 2.56 0 0 1 2.559-2.559 2.56 2.56 0 0 1 2.559 2.559v3.817c0 .68-.27 1.33-.75 1.811a2.56 2.56 0 0 1-3.619 0c-.48-.481-.75-1.131-.75-1.811v-3.817z" fill="#000"/><path d="M32.379 24.446c-1.188 0-2.325-.472-3.165-1.312s-1.312-1.977-1.312-3.165v-2.562a4.48 4.48 0 0 1 4.479-4.479h.003c1.188 0 2.325.472 3.165 1.312s1.312 1.977 1.312 3.165v2.562a4.48 4.48 0 0 1-4.479 4.479h-.002z" fill="#fff"/><path d="M34.301 16.768a2.56 2.56 0 0 0-2.56-2.559 2.56 2.56 0 0 0-2.559 2.559v3.817c0 .68.27 1.33.75 1.811a2.56 2.56 0 0 0 3.619 0c.48-.481.75-1.131.75-1.811v-3.817z" fill="#000"/></g><g clip-path="url(#A)"><g stroke="#000" class="C F"><path d="M123 45l6.02-24.078c.649-2.599 2.984-4.422 5.663-4.422h.002A16.82 16.82 0 0 1 151.5 33.315v.002c0 2.679-1.822 5.015-4.422 5.664L123 45z" class="D"/><g stroke-width="9" class="G"><use xlink:href="#B"/><use xlink:href="#C"/><path d="M150.75 30.75h1.667c1.863 0 3.7.433 5.366 1.267l3.467 1.733"/></g><g class="D"><path d="M142.5 13.5l3-3 3 3-3 3-3-3zm6-6L150 3l4.5 1.5L153 9l-4.5-1.5zM150 27l1.5-4.5L156 24l-1.5 4.5L150 27zM127.5 9l3-3 3 3-3 3-3-3zM159 19.5l3-3 3 3-3 3-3-3z"/></g></g><g class="B"><path d="M142.5 10.5l7.5-3h9V21h-24V9l7.5 1.5z" fill="#000"/><path d="M130.5 18.27l19.234 19.234a5.81 5.81 0 0 1-2.656 1.48L123 45.004l6.02-24.078a5.81 5.81 0 0 1 1.48-2.656z" fill="#e4ab1b"/><path d="M130.5 18.266c1.071-1.099 2.565-1.765 4.183-1.765h.002A16.82 16.82 0 0 1 151.5 33.315v.002c0 1.619-.666 3.113-1.766 4.184H148.5a17.99 17.99 0 0 1-18-18v-1.235h0z" fill="#5e3d05"/></g><g stroke-width="3" class="C F G"><use xlink:href="#B" stroke="#37cbe8"/><use xlink:href="#C" stroke="#f2c618"/><path d="M150.75 30.75h1.667c1.863 0 3.7.433 5.366 1.267l3.467 1.733" stroke="#ff4586"/></g><g class="B"><g fill="#9146dc"><path d="M142.5 13.5l3-3 3 3-3 3-3-3zm-15-4.5l3-3 3 3-3 3-3-3zM159 19.5l3-3 3 3-3 3-3-3z"/></g><path d="M148.5 7.5L150 3l4.5 1.5L153 9l-4.5-1.5z" fill="#f2298a"/><path d="M150 27l1.5-4.5L156 24l-1.5 4.5L150 27z" fill="#f2c618"/></g></g><path d="M96 34.5l9 9V45H93l-7.061-7.06a1.5 1.5 0 0 1-.439-1.061V6a3 3 0 0 1 3-3l7.025 14.051A4.48 4.48 0 0 1 96 19.062V34.5zm-24 0l-9 9V45h12l7.061-7.06a1.5 1.5 0 0 0 .439-1.061V6a3 3 0 0 0-3-3l-7.025 14.051A4.48 4.48 0 0 0 72 19.062V34.5z" stroke="#000" stroke-miterlimit="2" class="C D"/><g class="B"><path d="M72 34.5h3l3.75 3.75v3L75 45H63v-1.5l9-9zm24 0h-3l-3.75 3.75v3L93 45h12v-1.5l-9-9z" fill="#ce8d15"/><path d="M72 34.5l6.75 6.75 3.311-3.31a1.5 1.5 0 0 0 .439-1.061V6a3 3 0 0 0-3-3l-7.025 14.051A4.48 4.48 0 0 0 72 19.062V34.5z" class="E"/><path d="M82.5 36h-3a3 3 0 0 1-3-3V16.5c0-1.194.474-2.338 1.319-3.181S79.806 12 81 12l.224.006a4.49 4.49 0 0 1 2.958 1.313c.844.843 1.319 1.987 1.319 3.182V33a3 3 0 0 1-3 3zm-3-3h3V16.5c0-.398-.157-.78-.439-1.06S81.398 15 81 15s-.78.158-1.061.44-.439.663-.439 1.06V33z" fill="#765018"/><g class="E"><path d="M75 36v-4.5h10.5v6h-9L75 36z"/><path d="M96 34.5l-6.75 6.75-3.311-3.31a1.5 1.5 0 0 1-.439-1.061V6a3 3 0 0 1 3-3l7.025 14.051A4.48 4.48 0 0 1 96 19.062V34.5z"/></g><path d="M88.5 36a3 3 0 0 0 3-3V16.5c0-1.194-.474-2.338-1.319-3.181S88.194 12 87 12s-2.338.474-3.181 1.319S82.5 15.306 82.5 16.5V33a3 3 0 0 0 3 3h3zm0-3V16.5c0-.398-.157-.78-.439-1.06S87.398 15 87 15s-.78.158-1.061.44-.439.663-.439 1.06V33h3z" fill="#765018"/><path d="M91.5 37.5L93 36v-4.5H82.5v6h9z" class="E"/></g><defs><clipPath id="A"><path fill="#fff" transform="translate(120)" d="M0 0h48v48H0z"/></clipPath><path id="B" d="M142.5 25.5l5.068-2.533c2.538-1.27 4.545-3.398 5.663-6.007l.405-.944A9.1 9.1 0 0 1 162 10.5"/><path id="C" d="M136.5 19.5v-2.01a4.03 4.03 0 0 1 2.227-3.603c1.003-.503 1.763-1.379 2.117-2.439s.272-2.219-.229-3.218L139.5 6"/></defs></svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="168" height="48" fill="none" xmlns:v="https://vecta.io/nano"><style><![CDATA[.B{stroke:#000}.C{stroke-width:1.333}.D{stroke-linejoin:round}.E{fill:#fcea2b}.F{fill:#ea5a47}.G{stroke-linecap:round}.H{fill:#d22f27}.I{stroke-miterlimit:10}]]></style><use xlink:href="#B" class="E"/><use xlink:href="#C" fill="#fff"/><use xlink:href="#D" class="F"/><use xlink:href="#B" class="B C D"/><path d="M19.046 24.148c1.105 0 2-1.613 2-3.602s-.895-3.602-2-3.602-2 1.613-2 3.602.895 3.602 2 3.602zm9.908.002c1.105 0 2-1.613 2-3.602s-.895-3.602-2-3.602-2 1.613-2 3.602.895 3.602 2 3.602z" fill="#000"/><g class="B C D"><use xlink:href="#C"/><use xlink:href="#D"/></g><path d="M78.112 45.334H62.667v-7.778l10-5c-1.736 5.691 4.97 9.705 8.333 7.778l-2.889 5zm12.749-.001h15.445v-7.778l-10-5c1.736 5.691-4.97 9.705-8.333 7.778l2.889 5z" fill="#92d3f5"/><g class="E"><path d="M79.887 40.739c-4.117 0-7.449-3.193-7.449-7.138 4.417-2.68 4.854-9.226 5.312-12.976.333-1.927 1.194-1.927 1.5-3.854l1.75-11.873c.417-2.232 3.053-2.004 3.486-.232v31.278c0 1.29-1.931 2.333-2.597 2.333-.542 0-2.002 2.02-2.002 2.462z"/><path d="M89.086 40.739c4.117 0 7.449-3.193 7.449-7.138-4.417-2.68-4.854-9.226-5.312-12.976-.333-1.927-1.194-1.927-1.5-3.854l-1.75-11.873c-.417-2.232-3.053-2.004-3.486-.232v31.278c0 1.29 1.931 2.333 2.597 2.333.542 0 2.002 2.02 2.002 2.462z"/></g><g class="B C D I"><path d="M78.111 45.334H62.666v-7.778l10-5c-1.736 5.691 4.97 9.705 8.333 7.778l-2.889 5z"/><path d="M79.886 40.74c0-.442 1.46-2.462 2.002-2.462.667 0 2.597-1.044 2.597-2.333V4.667c-.433-1.772-3.069-2-3.486.232l-1.75 11.873c-.306 1.927-1.167 1.927-1.5 3.854-.458 3.75-.895 10.296-5.312 12.976" class="G"/><path d="M90.861 45.334h15.445v-7.778l-10-5c1.736 5.691-4.97 9.705-8.333 7.778l2.889 5z"/><path d="M89.085 40.74c0-.442-1.46-2.462-2.002-2.462-.667 0-2.597-1.044-2.597-2.333V4.667c.433-1.772 3.069-2 3.486.232l1.75 11.873c.305 1.927 1.167 1.927 1.5 3.854.458 3.75.895 10.296 5.312 12.976" class="G"/></g><path d="M137.453 13.454l8.38 8.379 8.379 8.38-14.353 5.974-14.353 5.974 5.974-14.353 5.973-14.353z" fill="#f1b31c"/><path d="M146.666 23.333l-9.538-9.554-6.201 14.58-6.2 14.58 21.939-19.606z" class="E"/><path d="M130.148 30.19l7.375 7.375-3.687 1.501-5.198-5.198 1.51-3.678z" class="F"/><path d="M131.562 36.832l2.274 2.234 3.687-1.501-3.2-3.201-2.761 2.468z" class="H"/><path d="M133.869 21.442l4.464 4.464 7.8 7.799-4.182 1.909-5.448-5.448-4.403-4.403 1.769-4.321z" class="F"/><path d="M137.69 31.354l4.261 4.26 4.182-1.909-5.226-5.226-3.217 2.874z" class="H"/><use xlink:href="#E" fill="#8967aa"/><use xlink:href="#E" x="20" y="2.667" fill="#f1b31c"/><use xlink:href="#E" x="18" y="16.667" class="H"/><g class="B C D G I"><path d="M153.775 30.426l.112.112-14.58 6.201-14.58 6.201 12.401-29.16"/><path d="M137.2 13.852l16.575 16.574M137.128 13.78l.072.072m13.793-8.94a3.26 3.26 0 0 1 .326.982c.301 1.832-.964 3.609-2.826 3.971"/><path d="M148.622 9.851a3.25 3.25 0 0 0-1.006.243c-1.713.714-2.552 2.729-1.874 4.499m15.453 3.218a3.24 3.24 0 0 1-.362.97c-.914 1.615-3.015 2.206-4.691 1.32m.107.07a3.24 3.24 0 0 0-.935-.442c-1.782-.518-3.699.524-4.282 2.329"/></g><defs ><path id="B" d="M24 39.333c8.468 0 15.333-6.865 15.333-15.333S32.469 8.667 24 8.667 8.667 15.532 8.667 24 15.532 39.333 24 39.333z"/><path id="C" d="M33.73 27.76a7.7 7.7 0 0 1-.58 2.993c-8.327 2.02-16.953.227-18.327-.087-.373-.923-.56-1.911-.553-2.907h.073s9.867 2.393 19.26.047l.127-.047z"/><path id="D" d="M33.15 30.753c-1.193 2.847-4.233 4.82-9.127 4.82-4.94 0-8.02-2.02-9.2-4.907 1.373.313 10 2.107 18.327.087z"/><path id="E" d="M140.197 10.952c.736 0 1.333-.588 1.333-1.314s-.597-1.314-1.333-1.314-1.334.588-1.334 1.314.597 1.314 1.334 1.314z"/></defs></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -1,528 +1,53 @@
import {
Reset,
Import,
FontFamily,
CodeAlt,
} from "@styled-icons/boxicons-regular";
import {
Pencil,
Store,
Palette,
HappyBeaming,
QuoteLeft,
} from "@styled-icons/boxicons-solid";
import { Link } from "react-router-dom";
// @ts-expect-error shade-blend-color does not have typings.
import pSBC from "shade-blend-color";
import { observer } from "mobx-react-lite";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { debounce } from "../../../lib/debounce";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import { isExperimentEnabled } from "../../../redux/reducers/experiments";
import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
import {
DEFAULT_FONT,
DEFAULT_MONO_FONT,
Fonts,
FONTS,
FONT_KEYS,
MonospaceFonts,
MONOSPACE_FONTS,
MONOSPACE_FONT_KEYS,
Theme,
ThemeContext,
ThemeOptions,
} from "../../../context/Theme";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import CollapsibleSection from "../../../components/common/CollapsibleSection";
import Tooltip from "../../../components/common/Tooltip";
import Button from "../../../components/ui/Button";
import Checkbox from "../../../components/ui/Checkbox";
import ColourSwatches from "../../../components/ui/ColourSwatches";
import ComboBox from "../../../components/ui/ComboBox";
import InputBox from "../../../components/ui/InputBox";
import CategoryButton from "../../../components/ui/fluent/CategoryButton";
import darkSVG from "../assets/dark.svg";
import lightSVG from "../assets/light.svg";
import mutantSVG from "../assets/mutant_emoji.svg";
import notoSVG from "../assets/noto_emoji.svg";
import openmojiSVG from "../assets/openmoji_emoji.svg";
import twemojiSVG from "../assets/twemoji_emoji.svg";
interface Props {
settings: Settings;
}
import {
ThemeBaseSelectorShim,
ThemeShopShim,
ThemeAccentShim,
DisplayFontShim,
DisplayMonospaceFontShim,
DisplayLigaturesShim,
DisplayEmojiShim,
ThemeCustomCSSShim,
} from "../../../components/settings/AppearanceShims";
import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides";
import ThemeTools from "../../../components/settings/appearance/ThemeTools";
// ! FIXME: code needs to be rewritten to fix jittering
export function Component(props: Props) {
const theme = useContext(ThemeContext);
const { writeClipboard, openScreen } = useIntermediate();
function setTheme(theme: ThemeOptions) {
dispatch({
type: "SETTINGS_SET_THEME",
theme,
});
}
const pushOverride = useCallback((custom: Partial<Theme>) => {
dispatch({
type: "SETTINGS_SET_THEME_OVERRIDE",
custom,
});
}, []);
function setAccent(accent: string) {
setOverride({
accent,
"scrollbar-thumb": pSBC(-0.2, accent),
});
}
const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
function setEmojiPack(emojiPack: EmojiPacks) {
dispatch({
type: "SETTINGS_SET_APPEARANCE",
options: {
emojiPack,
},
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const setOverride = useCallback(
debounce(pushOverride as (...args: unknown[]) => void, 200),
[pushOverride],
) as (custom: Partial<Theme>) => void;
const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
useEffect(() => setOverride({ css }), [setOverride, css]);
const selected = props.settings.theme?.base ?? "dark";
export const Appearance = observer(() => {
return (
<div className={styles.appearance}>
<h3>
<Text id="app.settings.pages.appearance.theme" />
</h3>
<div className={styles.themes}>
<div className={styles.theme}>
<img
loading="eager"
src={lightSVG}
draggable={false}
data-active={selected === "light"}
onClick={() =>
selected !== "light" && setTheme({ base: "light" })
}
onContextMenu={(e) => e.preventDefault()}
/>
<h4>
<Text id="app.settings.pages.appearance.color.light" />
</h4>
</div>
<div className={styles.theme}>
<img
loading="eager"
src={darkSVG}
draggable={false}
data-active={selected === "dark"}
onClick={() =>
selected !== "dark" && setTheme({ base: "dark" })
}
onContextMenu={(e) => e.preventDefault()}
/>
<h4>
<Text id="app.settings.pages.appearance.color.dark" />
</h4>
</div>
</div>
<ThemeBaseSelectorShim />
<ThemeShopShim />
<ThemeAccentShim />
{isExperimentEnabled("theme_shop") && (
<Link
to="/settings/theme_shop"
replace
className={styles.focus}>
<CategoryButton
icon={<Store size={24} />}
action="chevron"
description={"Browse themes made by the community"}
hover>
<Text id="app.settings.pages.theme_shop.title" />
</CategoryButton>
</Link>
)}
<DisplayFontShim />
<DisplayLigaturesShim />
<DisplayEmojiShim />
<h3>
<Text id="app.settings.pages.appearance.accent_selector" />
</h3>
<ColourSwatches value={theme.accent} onChange={setAccent} />
{/* TOFIX: Chane this checkbox to turn off the seasonal home page animations*/}
<Checkbox
checked={props.settings.theme?.ligatures === true}
onChange={() =>
setTheme({
ligatures: !props.settings.theme?.ligatures,
})
}
description={
"Displays effects in the home tab during holiday seasons."
}>
Seasonal theme
</Checkbox>
{/*<h3>
<Text id="app.settings.pages.appearance.message_display" />
</h3>
<div className={styles.display}>
<Radio
description={
<Text id="app.settings.pages.appearance.display.default_description" />
}
checked
>
<Text id="app.settings.pages.appearance.display.default" />
</Radio>
<Radio
description={
<Text id="app.settings.pages.appearance.display.compact_description" />
}
disabled
>
<Text id="app.settings.pages.appearance.display.compact" />
</Radio>
</div>*/}
<hr />
{/*<CategoryButton
icon={<Palette size={24} />}
description={"Customize the look of your app using themes."}
action="chevron">
Themes
</CategoryButton>
<CategoryButton
icon={<FontFamily size={24} />}
description={"Change the font and size used in the app."}
action="chevron">
{`Font & text size`}
</CategoryButton>
<CategoryButton
icon={<QuoteLeft size={24} />}
description={"Change the look of your messages."}
action="chevron">
Message Display
</CategoryButton>
<CategoryButton
icon={<HappyBeaming size={24} />}
description={"Personalize your client with an emoji pack."}
action="chevron">
Emoji Packs
</CategoryButton>
<h3>Advanced</h3>
<CategoryButton
icon={<CodeAlt size={24} />}
description={"Customize the client CSS to your heart's content"}
action="chevron">
Custom CSS
</CategoryButton>*/}
<h3>
<Text id="app.settings.pages.appearance.font" />
</h3>
<ComboBox
value={theme.font ?? DEFAULT_FONT}
onChange={(e) =>
pushOverride({ font: e.currentTarget.value as Fonts })
}>
{FONT_KEYS.map((key) => (
<option value={key} key={key}>
{FONTS[key as keyof typeof FONTS].name}
</option>
))}
</ComboBox>
{/* TOFIX: Only show when a font with ligature support is selected, i.e.: Inter.*/}
<Checkbox
checked={props.settings.theme?.ligatures === true}
onChange={() =>
setTheme({
ligatures: !props.settings.theme?.ligatures,
})
}
description={
<Text id="app.settings.pages.appearance.ligatures_desc" />
}>
<Text id="app.settings.pages.appearance.ligatures" />
</Checkbox>
<hr />
<h3>
<Text id="app.settings.pages.appearance.emoji_pack" />
</h3>
<div className={styles.emojiPack}>
<div className={styles.row}>
<div>
<div
className={styles.button}
onClick={() => setEmojiPack("mutant")}
data-active={emojiPack === "mutant"}>
<img
loading="eager"
src={mutantSVG}
draggable={false}
onContextMenu={(e) => e.preventDefault()}
/>
</div>
<h4>
Mutant Remix{" "}
<a
href="https://mutant.revolt.chat"
target="_blank"
rel="noreferrer">
(by Revolt)
</a>
</h4>
</div>
<div>
<div
className={styles.button}
onClick={() => setEmojiPack("twemoji")}
data-active={emojiPack === "twemoji"}>
<img
loading="eager"
src={twemojiSVG}
draggable={false}
onContextMenu={(e) => e.preventDefault()}
/>
</div>
<h4>Twemoji</h4>
</div>
</div>
<div className={styles.row}>
<div>
<div
className={styles.button}
onClick={() => setEmojiPack("openmoji")}
data-active={emojiPack === "openmoji"}>
<img
loading="eager"
src={openmojiSVG}
draggable={false}
onContextMenu={(e) => e.preventDefault()}
/>
</div>
<h4>Openmoji</h4>
</div>
<div>
<div
className={styles.button}
onClick={() => setEmojiPack("noto")}
data-active={emojiPack === "noto"}>
<img
loading="eager"
src={notoSVG}
draggable={false}
onContextMenu={(e) => e.preventDefault()}
/>
</div>
<h4>Noto Emoji</h4>
</div>
</div>
</div>
<hr />
<CollapsibleSection
defaultValue={false}
id="settings_overrides"
summary={<Text id="app.settings.pages.appearance.overrides" />}>
<div className={styles.actions}>
<Tooltip
content={
<Text id="app.settings.pages.appearance.reset_overrides" />
}>
<Button
contrast
iconbutton
onClick={() => setTheme({ custom: {} })}>
<Reset size={22} />
</Button>
</Tooltip>
<div
className={styles.code}
onClick={() => writeClipboard(JSON.stringify(theme))}>
<Tooltip content={<Text id="app.special.copy" />}>
{" "}
{/*TOFIX: Try to put the tooltip above the .code div without messing up the css challenge */}
{JSON.stringify(theme)}
</Tooltip>
</div>
<Tooltip
content={
<Text id="app.settings.pages.appearance.import" />
}>
<Button
contrast
iconbutton
onClick={async () => {
try {
const text =
await navigator.clipboard.readText();
setOverride(JSON.parse(text));
} catch (err) {
openScreen({
id: "_input",
question: (
<Text id="app.settings.pages.appearance.import_theme" />
),
field: (
<Text id="app.settings.pages.appearance.theme_data" />
),
callback: async (string) =>
setOverride(JSON.parse(string)),
});
}
}}>
<Import size={22} />
</Button>
</Tooltip>
</div>
<ThemeTools />
<h3>App</h3>
<div className={styles.overrides}>
{(
[
"accent",
"background",
"foreground",
"primary-background",
"primary-header",
"secondary-background",
"secondary-foreground",
"secondary-header",
"tertiary-background",
"tertiary-foreground",
"block",
"message-box",
"mention",
"scrollbar-thumb",
"scrollbar-track",
"status-online",
"status-away",
"status-busy",
"status-streaming",
"status-invisible",
"success",
"warning",
"error",
"hover",
] as const
).map((x) => (
<div
className={styles.entry}
key={x}
style={{ backgroundColor: theme[x] }}>
<div className={styles.input}>
<input
type="color"
value={theme[x]}
onChange={(v) =>
setOverride({
[x]: v.currentTarget.value,
})
}
/>
</div>
<span
style={`color: ${getContrastingColour(
theme[x],
theme["primary-background"],
)}`}>
{x}
</span>
<div className={styles.override}>
<div
className={styles.picker}
onClick={(e) =>
e.currentTarget.parentElement?.parentElement
?.querySelector("input")
?.click()
}>
<Pencil size={24} />
</div>
<InputBox
type="text"
className={styles.text}
value={theme[x]}
onChange={(y) =>
setOverride({
[x]: y.currentTarget.value,
})
}
/>
</div>
</div>
))}
</div>
<ThemeOverrides />
</CollapsibleSection>
<CollapsibleSection
id="settings_advanced_appearance"
defaultValue={false}
summary={<Text id="app.settings.pages.appearance.advanced" />}>
<h3>
<Text id="app.settings.pages.appearance.mono_font" />
</h3>
<ComboBox
value={theme.monospaceFont ?? DEFAULT_MONO_FONT}
onChange={(e) =>
pushOverride({
monospaceFont: e.currentTarget
.value as MonospaceFonts,
})
}>
{MONOSPACE_FONT_KEYS.map((key) => (
<option value={key} key={key}>
{
MONOSPACE_FONTS[
key as keyof typeof MONOSPACE_FONTS
].name
}
</option>
))}
</ComboBox>
<h3>
<Text id="app.settings.pages.appearance.custom_css" />
</h3>
<TextAreaAutoSize
maxRows={20}
minHeight={480}
code
value={css}
onChange={(ev) => setCSS(ev.currentTarget.value)}
/>
<DisplayMonospaceFontShim />
<ThemeCustomCSSShim />
</CollapsibleSection>
</div>
);
}
export const Appearance = connectState(Component, (state) => {
return {
settings: state.settings,
};
});
function getContrastingColour(hex: string, fallback: string): string {
hex = hex.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const cc = (r * 299 + g * 587 + b * 114) / 1000;
if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(cc))
return getContrastingColour(fallback, "#fffff");
return cc >= 175 ? "black" : "white";
}
// <DisplayCompactShim />

View File

@@ -6,8 +6,6 @@ import { TextReact } from "../../../lib/i18n";
import { stopPropagation } from "../../../lib/stopPropagation";
import { voiceState } from "../../../lib/vortex/VoiceState";
import { connectState } from "../../../redux/connector";
import Button from "../../../components/ui/Button";
import ComboBox from "../../../components/ui/ComboBox";
import Overline from "../../../components/ui/Overline";
@@ -20,7 +18,9 @@ import opusSVG from "../assets/opus_logo.svg";
const constraints = { audio: true };
export function Component() {
// TODO: do not rewrite this code until voice is rewritten!
export function Audio() {
const [mediaStream, setMediaStream] = useState<MediaStream | undefined>(
undefined,
);
@@ -244,7 +244,3 @@ function changeAudioDevice(deviceId: string, deviceType: string) {
window.localStorage.setItem("audioOutputDevice", deviceId);
}
}
export const Audio = connectState(Component, () => {
return;
});

View File

@@ -1,22 +1,19 @@
import { observer } from "mobx-react-lite";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import { useApplicationState } from "../../../mobx/State";
import {
AVAILABLE_EXPERIMENTS,
ExperimentOptions,
EXPERIMENTS,
isExperimentEnabled,
} from "../../../redux/reducers/experiments";
} from "../../../mobx/stores/Experiments";
import Checkbox from "../../../components/ui/Checkbox";
interface Props {
options?: ExperimentOptions;
}
export const ExperimentsPage = observer(() => {
const experiments = useApplicationState().experiments;
export function Component(props: Props) {
return (
<div className={styles.experiments}>
<h3>
@@ -25,15 +22,8 @@ export function Component(props: Props) {
{AVAILABLE_EXPERIMENTS.map((key) => (
<Checkbox
key={key}
checked={isExperimentEnabled(key, props.options)}
onChange={(enabled) =>
dispatch({
type: enabled
? "EXPERIMENTS_ENABLE"
: "EXPERIMENTS_DISABLE",
key,
})
}
checked={experiments.isEnabled(key)}
onChange={(enabled) => experiments.setEnabled(key, enabled)}
description={EXPERIMENTS[key].description}>
{EXPERIMENTS[key].title}
</Checkbox>
@@ -45,10 +35,4 @@ export function Component(props: Props) {
)}
</div>
);
}
export const ExperimentsPage = connectState(Component, (state) => {
return {
options: state.experiments,
};
});

View File

@@ -1,8 +1,10 @@
import { observer } from "mobx-react-lite";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { useMemo } from "preact/hooks";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import { useApplicationState } from "../../../mobx/State";
import {
Language,
@@ -17,26 +19,25 @@ import enchantingTableWEBP from "../assets/enchanting_table.webp";
import tamilFlagPNG from "../assets/tamil_nadu_flag.png";
import tokiponaSVG from "../assets/toki_pona.svg";
type Props = {
locale: Language;
};
type Key = [Language, LanguageEntry];
type Key = [string, LanguageEntry];
interface Props {
entry: Key;
selected: boolean;
onSelect: () => void;
}
function Entry({ entry: [x, lang], locale }: { entry: Key } & Props) {
/**
* Component providing individual language entries.
* @param param0 Entry data
*/
function Entry({ entry: [x, lang], selected, onSelect }: Props) {
return (
<Checkbox
key={x}
className={styles.entry}
checked={locale === x}
onChange={(v) => {
if (v) {
dispatch({
type: "SET_LOCALE",
locale: x as Language,
});
}
}}>
checked={selected}
onChange={onSelect}>
<div className={styles.flag}>
{lang.i18n === "ta" ? (
<img
@@ -61,36 +62,58 @@ function Entry({ entry: [x, lang], locale }: { entry: Key } & Props) {
);
}
export function Component(props: Props) {
const languages = Object.keys(Langs).map((x) => [
x,
Langs[x as keyof typeof Langs],
]) as Key[];
/**
* Component providing the language selection menu.
*/
export const Languages = observer(() => {
const locale = useApplicationState().locale;
const language = locale.getLanguage();
// Get the user's system language. Check for exact
// matches first, otherwise check for partial matches
const preferredLanguage =
navigator.languages.filter((lang) =>
languages.find((l) => l[0].replace(/_/g, "-") == lang),
)?.[0] ||
navigator.languages
?.map((x) => x.split("-")[0])
?.filter((lang) => languages.find((l) => l[0] == lang))?.[0]
?.split("-")[0];
// Generate languages array.
const languages = useMemo(() => {
const languages = Object.keys(Langs).map((x) => [
x,
Langs[x as keyof typeof Langs],
]) as Key[];
if (preferredLanguage) {
// This moves the user's system language to the top of the language list
const prefLangKey = languages.find(
(lang) => lang[0].replace(/_/g, "-") == preferredLanguage,
);
if (prefLangKey) {
languages.splice(
0,
0,
languages.splice(languages.indexOf(prefLangKey), 1)[0],
// Get the user's system language. Check for exact
// matches first, otherwise check for partial matches
const preferredLanguage =
navigator.languages.filter((lang) =>
languages.find((l) => l[0].replace(/_/g, "-") == lang),
)?.[0] ||
navigator.languages
?.map((x) => x.split("-")[0])
?.filter((lang) => languages.find((l) => l[0] == lang))?.[0]
?.split("-")[0];
if (preferredLanguage) {
// This moves the user's system language to the top of the language list
const prefLangKey = languages.find(
(lang) => lang[0].replace(/_/g, "-") == preferredLanguage,
);
if (prefLangKey) {
languages.splice(
0,
0,
languages.splice(languages.indexOf(prefLangKey), 1)[0],
);
}
}
}
return languages;
}, []);
// Creates entries with given key.
const EntryFactory = ([x, lang]: Key) => (
<Entry
key={x}
entry={[x, lang]}
selected={language === x}
onSelect={() => locale.setLanguage(x)}
/>
);
return (
<div className={styles.languages}>
@@ -98,11 +121,7 @@ export function Component(props: Props) {
<Text id="app.settings.pages.language.select" />
</h3>
<div className={styles.list}>
{languages
.filter(([, lang]) => !lang.cat)
.map(([x, lang]) => (
<Entry key={x} entry={[x, lang]} {...props} />
))}
{languages.filter(([, lang]) => !lang.cat).map(EntryFactory)}
</div>
<h3>
<Text id="app.settings.pages.language.const" />
@@ -110,9 +129,7 @@ export function Component(props: Props) {
<div className={styles.list}>
{languages
.filter(([, lang]) => lang.cat === "const")
.map(([x, lang]) => (
<Entry key={x} entry={[x, lang]} {...props} />
))}
.map(EntryFactory)}
</div>
<h3>
<Text id="app.settings.pages.language.other" />
@@ -120,9 +137,7 @@ export function Component(props: Props) {
<div className={styles.list}>
{languages
.filter(([, lang]) => lang.cat === "alt")
.map(([x, lang]) => (
<Entry key={x} entry={[x, lang]} {...props} />
))}
.map(EntryFactory)}
</div>
<Tip>
<span>
@@ -137,10 +152,4 @@ export function Component(props: Props) {
</Tip>
</div>
);
}
export const Languages = connectState(Component, (state) => {
return {
locale: state.locale,
};
});

View File

@@ -1,4 +1,4 @@
import defaultsDeep from "lodash.defaultsdeep";
import { observer } from "mobx-react-lite";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
@@ -6,28 +6,17 @@ import { useContext, useEffect, useState } from "preact/hooks";
import { urlBase64ToUint8Array } from "../../../lib/conversion";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import {
DEFAULT_SOUNDS,
NotificationOptions,
SoundOptions,
} from "../../../redux/reducers/settings";
import { useApplicationState } from "../../../mobx/State";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import Checkbox from "../../../components/ui/Checkbox";
import { SOUNDS_ARRAY } from "../../../assets/sounds/Audio";
interface Props {
options?: NotificationOptions;
}
export function Component({ options }: Props) {
export const Notifications = observer(() => {
const client = useContext(AppContext);
const { openScreen } = useIntermediate();
const settings = useApplicationState().settings;
const [pushEnabled, setPushEnabled] = useState<undefined | boolean>(
undefined,
);
@@ -42,10 +31,6 @@ export function Component({ options }: Props) {
});
}, []);
const enabledSounds: SoundOptions = defaultsDeep(
options?.sounds ?? {},
DEFAULT_SOUNDS,
);
return (
<div className={styles.notifications}>
<h3>
@@ -53,7 +38,7 @@ export function Component({ options }: Props) {
</h3>
<Checkbox
disabled={!("Notification" in window)}
checked={options?.desktopEnabled ?? false}
checked={settings.get("notifications:desktop", false)!}
description={
<Text id="app.settings.pages.notifications.descriptions.enable_desktop" />
}
@@ -61,6 +46,7 @@ export function Component({ options }: Props) {
if (desktopEnabled) {
const permission =
await Notification.requestPermission();
if (permission !== "granted") {
return openScreen({
id: "error",
@@ -69,10 +55,7 @@ export function Component({ options }: Props) {
}
}
dispatch({
type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
options: { desktopEnabled },
});
settings.set("notifications:desktop", desktopEnabled);
}}>
<Text id="app.settings.pages.notifications.enable_desktop" />
</Checkbox>
@@ -125,32 +108,16 @@ export function Component({ options }: Props) {
<h3>
<Text id="app.settings.pages.notifications.sounds" />
</h3>
{SOUNDS_ARRAY.map((key) => (
{settings.sounds.getState().map(({ id, enabled }) => (
<Checkbox
key={key}
checked={!!enabledSounds[key]}
key={id}
checked={enabled}
onChange={(enabled) =>
dispatch({
type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
options: {
sounds: {
...options?.sounds,
[key]: enabled,
},
},
})
settings.sounds.setEnabled(id, enabled)
}>
<Text
id={`app.settings.pages.notifications.sound.${key}`}
/>
<Text id={`app.settings.pages.notifications.sound.${id}`} />
</Checkbox>
))}
</div>
);
}
export const Notifications = connectState(Component, (state) => {
return {
options: state.settings.notification,
};
});

View File

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

View File

@@ -1,17 +1,16 @@
import { observer } from "mobx-react-lite";
import styles from "./Panes.module.scss";
import { Text } from "preact-i18n";
import { dispatch } from "../../../redux";
import { connectState } from "../../../redux/connector";
import { SyncKeys, SyncOptions } from "../../../redux/reducers/sync";
import { useApplicationState } from "../../../mobx/State";
import { SyncKeys } from "../../../mobx/stores/Sync";
import Checkbox from "../../../components/ui/Checkbox";
interface Props {
options?: SyncOptions;
}
export const Sync = observer(() => {
const sync = useApplicationState().sync;
export function Component(props: Props) {
return (
<div className={styles.notifications}>
{/*<h3>
@@ -31,22 +30,13 @@ export function Component(props: Props) {
).map(([key, title]) => (
<Checkbox
key={key}
checked={
(props.options?.disabled ?? []).indexOf(key) === -1
}
checked={sync.isEnabled(key)}
description={
<Text
id={`app.settings.pages.sync.descriptions.${key}`}
/>
}
onChange={(enabled) =>
dispatch({
type: enabled
? "SYNC_ENABLE_KEY"
: "SYNC_DISABLE_KEY",
key,
})
}>
onChange={() => sync.toggle(key)}>
<Text id={`app.settings.pages.${title}`} />
</Checkbox>
))}
@@ -55,10 +45,4 @@ export function Component(props: Props) {
</h5>*/}
</div>
);
}
export const Sync = connectState(Component, (state) => {
return {
options: state.sync,
};
});

View File

@@ -1,19 +1,11 @@
import { Plus, Check } from "@styled-icons/boxicons-regular";
import {
Star,
BarChartAlt2,
Brush,
Bookmark,
} from "@styled-icons/boxicons-solid";
import styled from "styled-components";
import { useEffect, useState } from "preact/hooks";
import { dispatch } from "../../../redux";
import { useApplicationState } from "../../../mobx/State";
import { Theme, generateVariables, ThemeOptions } from "../../../context/Theme";
import { Theme, generateVariables } from "../../../context/Theme";
import InputBox from "../../../components/ui/InputBox";
import Tip from "../../../components/ui/Tip";
import previewPath from "../assets/preview.svg";
@@ -258,6 +250,8 @@ export function ThemeShop() {
>(null);
const [themeData, setThemeData] = useState<Record<string, Theme>>({});
const themes = useApplicationState().settings.theme;
async function fetchThemeList() {
const manifest = await fetchManifest();
setThemeList(
@@ -352,21 +346,9 @@ export function ThemeShop() {
data-loaded={Reflect.has(themeData, slug)}>
<button
class="preview"
onClick={() => {
dispatch({
type: "THEMES_SET_THEME",
theme: {
slug,
meta: theme,
theme: themeData[slug],
},
});
dispatch({
type: "SETTINGS_SET_THEME",
theme: { base: slug },
});
}}>
onClick={() =>
themes.hydrate(themeData[slug], true)
}>
<div class="previewBox">
<div class="hover">Use theme</div>
<ThemePreview