mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 09:25:27 +00:00
Merge branch 'mobx'
This commit is contained in:
@@ -6,7 +6,8 @@ import styled from "styled-components";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useState } from "preact/hooks";
|
||||
|
||||
import { dispatch, getState } from "../../redux";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
import { SECTION_NSFW } from "../../mobx/stores/Layout";
|
||||
|
||||
import Button from "../ui/Button";
|
||||
import Checkbox from "../ui/Checkbox";
|
||||
@@ -49,9 +50,7 @@ type Props = {
|
||||
|
||||
export default observer((props: Props) => {
|
||||
const history = useHistory();
|
||||
const [consent, setConsent] = useState(
|
||||
getState().sectionToggle["nsfw"] ?? false,
|
||||
);
|
||||
const layout = useApplicationState().layout;
|
||||
const [ageGate, setAgeGate] = useState(false);
|
||||
|
||||
if (ageGate || !props.gated) {
|
||||
@@ -81,26 +80,19 @@ export default observer((props: Props) => {
|
||||
</span>
|
||||
|
||||
<Checkbox
|
||||
checked={consent}
|
||||
onChange={(v) => {
|
||||
setConsent(v);
|
||||
if (v) {
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_SET",
|
||||
id: "nsfw",
|
||||
state: true,
|
||||
});
|
||||
} else {
|
||||
dispatch({ type: "SECTION_TOGGLE_UNSET", id: "nsfw" });
|
||||
}
|
||||
}}>
|
||||
checked={layout.getSectionState(SECTION_NSFW, false)}
|
||||
onChange={() => layout.toggleSectionState(SECTION_NSFW, false)}>
|
||||
<Text id="app.main.channel.nsfw.confirm" />
|
||||
</Checkbox>
|
||||
<div className="actions">
|
||||
<Button contrast onClick={() => history.goBack()}>
|
||||
<Text id="app.special.modals.actions.back" />
|
||||
</Button>
|
||||
<Button contrast onClick={() => consent && setAgeGate(true)}>
|
||||
<Button
|
||||
contrast
|
||||
onClick={() =>
|
||||
layout.getSectionState(SECTION_NSFW) && setAgeGate(true)
|
||||
}>
|
||||
<Text id={`app.main.channel.nsfw.${props.type}.confirm`} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ChevronDown } from "@styled-icons/boxicons-regular";
|
||||
|
||||
import { State, store } from "../../redux";
|
||||
import { Action } from "../../redux/reducers";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import Details from "../ui/Details";
|
||||
|
||||
@@ -25,27 +24,14 @@ export default function CollapsibleSection({
|
||||
children,
|
||||
...detailsProps
|
||||
}: Props) {
|
||||
const state: State = store.getState();
|
||||
|
||||
function setState(state: boolean) {
|
||||
if (state === defaultValue) {
|
||||
store.dispatch({
|
||||
type: "SECTION_TOGGLE_UNSET",
|
||||
id,
|
||||
} as Action);
|
||||
} else {
|
||||
store.dispatch({
|
||||
type: "SECTION_TOGGLE_SET",
|
||||
id,
|
||||
state,
|
||||
} as Action);
|
||||
}
|
||||
}
|
||||
const layout = useApplicationState().layout;
|
||||
|
||||
return (
|
||||
<Details
|
||||
open={state.sectionToggle[id] ?? defaultValue}
|
||||
onToggle={(e) => setState(e.currentTarget.open)}
|
||||
open={layout.getSectionState(id, defaultValue)}
|
||||
onToggle={(e) =>
|
||||
layout.setSectionState(id, e.currentTarget.open, defaultValue)
|
||||
}
|
||||
{...detailsProps}>
|
||||
<summary>
|
||||
<div class="padding">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { EmojiPacks } from "../../redux/reducers/settings";
|
||||
export type EmojiPack = "mutant" | "twemoji" | "noto" | "openmoji";
|
||||
|
||||
let EMOJI_PACK = "mutant";
|
||||
let EMOJI_PACK: EmojiPack = "mutant";
|
||||
const REVISION = 3;
|
||||
|
||||
export function setEmojiPack(pack: EmojiPacks) {
|
||||
export function setGlobalEmojiPack(pack: EmojiPack) {
|
||||
EMOJI_PACK = pack;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import { dispatch } from "../../redux";
|
||||
import { connectState } from "../../redux/connector";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import { Language, Languages } from "../../context/Locale";
|
||||
|
||||
import ComboBox from "../ui/ComboBox";
|
||||
|
||||
type Props = {
|
||||
locale: string;
|
||||
};
|
||||
/**
|
||||
* Component providing a language selector combobox.
|
||||
* Note: this is not an observer but this is fine as we are just using a combobox.
|
||||
*/
|
||||
export default function LocaleSelector() {
|
||||
const locale = useApplicationState().locale;
|
||||
|
||||
export function LocaleSelector(props: Props) {
|
||||
return (
|
||||
<ComboBox
|
||||
value={props.locale}
|
||||
value={locale.getLanguage()}
|
||||
onChange={(e) =>
|
||||
dispatch({
|
||||
type: "SET_LOCALE",
|
||||
locale: e.currentTarget.value as Language,
|
||||
})
|
||||
locale.setLanguage(e.currentTarget.value as Language)
|
||||
}>
|
||||
{Object.keys(Languages).map((x) => {
|
||||
const l = Languages[x as keyof typeof Languages];
|
||||
@@ -30,9 +28,3 @@ export function LocaleSelector(props: Props) {
|
||||
</ComboBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectState(LocaleSelector, (state) => {
|
||||
return {
|
||||
locale: state.locale,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Download, CloudDownload } from "@styled-icons/boxicons-regular";
|
||||
|
||||
import { useContext, useEffect, useState } from "preact/hooks";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { internalSubscribe } from "../../lib/eventEmitter";
|
||||
|
||||
import { ThemeContext } from "../../context/Theme";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import IconButton from "../ui/IconButton";
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function UpdateIndicator({ style }: Props) {
|
||||
});
|
||||
|
||||
if (!pending) return null;
|
||||
const theme = useContext(ThemeContext);
|
||||
const theme = useApplicationState().settings.theme;
|
||||
|
||||
if (style === "titlebar") {
|
||||
return (
|
||||
@@ -36,7 +36,10 @@ export default function UpdateIndicator({ style }: Props) {
|
||||
content="A new update is available!"
|
||||
placement="bottom">
|
||||
<div onClick={() => updateSW(true)}>
|
||||
<CloudDownload size={22} color={theme.success} />
|
||||
<CloudDownload
|
||||
size={22}
|
||||
color={theme.getVariable("success")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -47,7 +50,7 @@ export default function UpdateIndicator({ style }: Props) {
|
||||
|
||||
return (
|
||||
<IconButton onClick={() => updateSW(true)}>
|
||||
<Download size={22} color={theme.success} />
|
||||
<Download size={22} color={theme.getVariable("success")} />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useState } from "preact/hooks";
|
||||
|
||||
import { internalEmit } from "../../../lib/eventEmitter";
|
||||
|
||||
import { QueuedMessage } from "../../../redux/reducers/queue";
|
||||
import { QueuedMessage } from "../../../mobx/stores/MessageQueue";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
@@ -20,10 +20,9 @@ import {
|
||||
SMOOTH_SCROLL_ON_RECEIVE,
|
||||
} from "../../../lib/renderer/Singleton";
|
||||
|
||||
import { dispatch, getState } from "../../../redux";
|
||||
import { Reply } from "../../../redux/reducers/queue";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
import { Reply } from "../../../mobx/stores/MessageQueue";
|
||||
|
||||
import { SoundContext } from "../../../context/Settings";
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import {
|
||||
FileUploader,
|
||||
@@ -112,17 +111,16 @@ const Action = styled.div`
|
||||
const RE_SED = new RegExp("^s/([^])*/([^])*$");
|
||||
|
||||
// ! FIXME: add to app config and load from app config
|
||||
export const CAN_UPLOAD_AT_ONCE = 4;
|
||||
export const CAN_UPLOAD_AT_ONCE = 5;
|
||||
|
||||
export default observer(({ channel }: Props) => {
|
||||
const [draft, setDraft] = useState(getState().drafts[channel._id] ?? "");
|
||||
const state = useApplicationState();
|
||||
|
||||
const [uploadState, setUploadState] = useState<UploadState>({
|
||||
type: "none",
|
||||
});
|
||||
const [typing, setTyping] = useState<boolean | number>(false);
|
||||
const [replies, setReplies] = useState<Reply[]>([]);
|
||||
const playSound = useContext(SoundContext);
|
||||
const { openScreen } = useIntermediate();
|
||||
const client = useContext(AppContext);
|
||||
const translate = useTranslation();
|
||||
@@ -148,27 +146,18 @@ export default observer(({ channel }: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Push message content to draft.
|
||||
const setMessage = useCallback(
|
||||
(content?: string) => {
|
||||
setDraft(content ?? "");
|
||||
|
||||
if (content) {
|
||||
dispatch({
|
||||
type: "SET_DRAFT",
|
||||
channel: channel._id,
|
||||
content,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "CLEAR_DRAFT",
|
||||
channel: channel._id,
|
||||
});
|
||||
}
|
||||
},
|
||||
[channel._id],
|
||||
(content?: string) => state.draft.set(channel._id, content),
|
||||
[state.draft, channel._id],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
*
|
||||
* @param content
|
||||
* @param action
|
||||
*/
|
||||
function append(content: string, action: "quote" | "mention") {
|
||||
const text =
|
||||
action === "quote"
|
||||
@@ -178,10 +167,10 @@ export default observer(({ channel }: Props) => {
|
||||
.join("\n")}\n\n`
|
||||
: `${content} `;
|
||||
|
||||
if (!draft || draft.length === 0) {
|
||||
if (!state.draft.has(channel._id)) {
|
||||
setMessage(text);
|
||||
} else {
|
||||
setMessage(`${draft}\n${text}`);
|
||||
setMessage(`${state.draft.get(channel._id)}\n${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,13 +179,16 @@ export default observer(({ channel }: Props) => {
|
||||
"append",
|
||||
append as (...args: unknown[]) => void,
|
||||
);
|
||||
}, [draft, setMessage]);
|
||||
}, [state.draft, channel._id, setMessage]);
|
||||
|
||||
/**
|
||||
* Trigger send message.
|
||||
*/
|
||||
async function send() {
|
||||
if (uploadState.type === "uploading" || uploadState.type === "sending")
|
||||
return;
|
||||
|
||||
const content = draft?.trim() ?? "";
|
||||
const content = state.draft.get(channel._id)?.trim() ?? "";
|
||||
if (uploadState.type === "attached") return sendFile(content);
|
||||
if (content.length === 0) return;
|
||||
|
||||
@@ -247,20 +239,15 @@ export default observer(({ channel }: Props) => {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
playSound("outbound");
|
||||
state.settings.sounds.playSound("outbound");
|
||||
|
||||
dispatch({
|
||||
type: "QUEUE_ADD",
|
||||
nonce,
|
||||
state.queue.add(nonce, channel._id, {
|
||||
_id: nonce,
|
||||
channel: channel._id,
|
||||
message: {
|
||||
_id: nonce,
|
||||
channel: channel._id,
|
||||
author: client.user!._id,
|
||||
author: client.user!._id,
|
||||
|
||||
content,
|
||||
replies,
|
||||
},
|
||||
content,
|
||||
replies,
|
||||
});
|
||||
|
||||
defer(() => renderer.jumpToBottom(SMOOTH_SCROLL_ON_RECEIVE));
|
||||
@@ -272,15 +259,16 @@ export default observer(({ channel }: Props) => {
|
||||
replies,
|
||||
});
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: "QUEUE_FAIL",
|
||||
error: takeError(error),
|
||||
nonce,
|
||||
});
|
||||
state.queue.fail(nonce, takeError(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param content
|
||||
* @returns
|
||||
*/
|
||||
async function sendFile(content: string) {
|
||||
if (uploadState.type !== "attached") return;
|
||||
const attachments: string[] = [];
|
||||
@@ -360,7 +348,7 @@ export default observer(({ channel }: Props) => {
|
||||
|
||||
setMessage();
|
||||
setReplies([]);
|
||||
playSound("outbound");
|
||||
state.settings.sounds.playSound("outbound");
|
||||
|
||||
if (files.length > CAN_UPLOAD_AT_ONCE) {
|
||||
setUploadState({
|
||||
@@ -372,6 +360,10 @@ export default observer(({ channel }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
function startTyping() {
|
||||
if (typeof typing === "number" && +new Date() < typing) return;
|
||||
|
||||
@@ -385,6 +377,10 @@ export default observer(({ channel }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param force
|
||||
*/
|
||||
function stopTyping(force?: boolean) {
|
||||
if (force || typing) {
|
||||
const ws = client.websocket;
|
||||
@@ -503,7 +499,7 @@ export default observer(({ channel }: Props) => {
|
||||
id="message"
|
||||
maxLength={2000}
|
||||
onKeyUp={onKeyUp}
|
||||
value={draft ?? ""}
|
||||
value={state.draft.get(channel._id) ?? ""}
|
||||
padding="var(--message-box-padding)"
|
||||
onKeyDown={(e) => {
|
||||
if (e.ctrlKey && e.key === "Enter") {
|
||||
@@ -515,7 +511,7 @@ export default observer(({ channel }: Props) => {
|
||||
|
||||
if (
|
||||
e.key === "ArrowUp" &&
|
||||
(!draft || draft.length === 0)
|
||||
!state.draft.has(channel._id)
|
||||
) {
|
||||
e.preventDefault();
|
||||
internalEmit("MessageRenderer", "edit_last");
|
||||
|
||||
@@ -10,8 +10,9 @@ import { StateUpdater, useEffect } from "preact/hooks";
|
||||
|
||||
import { internalSubscribe } from "../../../../lib/eventEmitter";
|
||||
|
||||
import { dispatch, getState } from "../../../../redux";
|
||||
import { Reply } from "../../../../redux/reducers/queue";
|
||||
import { useApplicationState } from "../../../../mobx/State";
|
||||
import { SECTION_MENTION } from "../../../../mobx/stores/Layout";
|
||||
import { Reply } from "../../../../mobx/stores/MessageQueue";
|
||||
|
||||
import IconButton from "../../../ui/IconButton";
|
||||
|
||||
@@ -81,6 +82,7 @@ const Base = styled.div`
|
||||
const MAX_REPLIES = 5;
|
||||
export default observer(({ channel, replies, setReplies }: Props) => {
|
||||
const client = channel.client;
|
||||
const layout = useApplicationState().layout;
|
||||
|
||||
// Event listener for adding new messages to reply bar.
|
||||
useEffect(() => {
|
||||
@@ -99,7 +101,7 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
||||
mention:
|
||||
message.author_id === client.user!._id
|
||||
? false
|
||||
: getState().sectionToggle.mention ?? false,
|
||||
: layout.getSectionState("SECTION_MENTION", false),
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -181,11 +183,11 @@ export default observer(({ channel, replies, setReplies }: Props) => {
|
||||
}),
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_SET",
|
||||
id: "mention",
|
||||
layout.setSectionState(
|
||||
SECTION_MENTION,
|
||||
state,
|
||||
});
|
||||
false,
|
||||
);
|
||||
}}>
|
||||
<span class="toggle">
|
||||
<At size={15} />
|
||||
|
||||
@@ -10,8 +10,6 @@ import { useContext, useEffect, useState } from "preact/hooks";
|
||||
import { defer } from "../../../../lib/defer";
|
||||
import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../../redux";
|
||||
|
||||
import {
|
||||
AppContext,
|
||||
ClientStatus,
|
||||
@@ -33,7 +31,7 @@ const EmbedInviteBase = styled.div`
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
margin-top: 2px;
|
||||
${() =>
|
||||
${() =>
|
||||
isTouchscreenDevice &&
|
||||
css`
|
||||
flex-wrap: wrap;
|
||||
@@ -44,19 +42,17 @@ const EmbedInviteBase = styled.div`
|
||||
> button {
|
||||
width: 100%;
|
||||
}
|
||||
`
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const EmbedInviteDetails = styled.div`
|
||||
flex-grow: 1;
|
||||
padding-left: 12px;
|
||||
${() =>
|
||||
${() =>
|
||||
isTouchscreenDevice &&
|
||||
css`
|
||||
width: calc(100% - 55px);
|
||||
`
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const EmbedInviteName = styled.div`
|
||||
@@ -74,11 +70,10 @@ type Props = {
|
||||
code: string;
|
||||
};
|
||||
|
||||
export function EmbedInvite(props: Props) {
|
||||
export function EmbedInvite({ code }: Props) {
|
||||
const history = useHistory();
|
||||
const client = useContext(AppContext);
|
||||
const status = useContext(StatusContext);
|
||||
const code = props.code;
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [joinError, setJoinError] = useState<string | undefined>(undefined);
|
||||
@@ -124,7 +119,8 @@ export function EmbedInvite(props: Props) {
|
||||
<EmbedInviteDetails>
|
||||
<EmbedInviteName>{invite.server_name}</EmbedInviteName>
|
||||
<EmbedInviteMemberCount>
|
||||
{invite.member_count.toLocaleString()} {invite.member_count === 1 ? "member" : "members"}
|
||||
{invite.member_count.toLocaleString()}{" "}
|
||||
{invite.member_count === 1 ? "member" : "members"}
|
||||
</EmbedInviteMemberCount>
|
||||
</EmbedInviteDetails>
|
||||
{processing ? (
|
||||
@@ -151,10 +147,9 @@ export function EmbedInvite(props: Props) {
|
||||
|
||||
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}`,
|
||||
@@ -172,7 +167,9 @@ export function EmbedInvite(props: Props) {
|
||||
setProcessing(false);
|
||||
}
|
||||
}}>
|
||||
{client.servers.get(invite.server_id) ? "Joined" : "Join"}
|
||||
{client.servers.get(invite.server_id)
|
||||
? "Joined"
|
||||
: "Join"}
|
||||
</Button>
|
||||
)}
|
||||
</EmbedInviteBase>
|
||||
|
||||
@@ -5,12 +5,10 @@ import { useParams } from "react-router-dom";
|
||||
import { Masquerade } from "revolt-api/types/Channels";
|
||||
import { Presence } from "revolt-api/types/Users";
|
||||
import { User } from "revolt.js/dist/maps/Users";
|
||||
import { Nullable } from "revolt.js/dist/util/null";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { useContext } from "preact/hooks";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { ThemeContext } from "../../../context/Theme";
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import fallback from "../assets/user.png";
|
||||
@@ -26,15 +24,15 @@ interface Props extends IconBaseProps<User> {
|
||||
}
|
||||
|
||||
export function useStatusColour(user?: User) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const theme = useApplicationState().settings.theme;
|
||||
|
||||
return user?.online && user?.status?.presence !== Presence.Invisible
|
||||
? user?.status?.presence === Presence.Idle
|
||||
? theme["status-away"]
|
||||
? theme.getVariable("status-away")
|
||||
: user?.status?.presence === Presence.Busy
|
||||
? theme["status-busy"]
|
||||
: theme["status-online"]
|
||||
: theme["status-invisible"];
|
||||
? theme.getVariable("status-busy")
|
||||
: theme.getVariable("status-online")
|
||||
: theme.getVariable("status-invisible");
|
||||
}
|
||||
|
||||
const VoiceIndicator = styled.div<{ status: VoiceStatus }>`
|
||||
|
||||
@@ -5,8 +5,7 @@ import styled, { css } from "styled-components";
|
||||
|
||||
import ConditionalLink from "../../lib/ConditionalLink";
|
||||
|
||||
import { connectState } from "../../redux/connector";
|
||||
import { LastOpened } from "../../redux/reducers/last_opened";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||
|
||||
@@ -47,19 +46,14 @@ const Button = styled.a<{ active: boolean }>`
|
||||
`}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
lastOpened: LastOpened;
|
||||
}
|
||||
|
||||
export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const layout = useApplicationState().layout;
|
||||
const user = client.users.get(client.user!._id);
|
||||
|
||||
const history = useHistory();
|
||||
const path = useLocation().pathname;
|
||||
|
||||
const channel_id = lastOpened["home"];
|
||||
|
||||
const friendsActive = path.startsWith("/friends");
|
||||
const settingsActive = path.startsWith("/settings");
|
||||
const homeActive = !(friendsActive || settingsActive);
|
||||
@@ -73,14 +67,11 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
if (settingsActive) {
|
||||
if (history.length > 0) {
|
||||
history.goBack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel_id) {
|
||||
history.push(`/channel/${channel_id}`);
|
||||
} else {
|
||||
history.push("/");
|
||||
}
|
||||
history.push(layout.getLastHomePath());
|
||||
}}>
|
||||
<Message size={24} />
|
||||
</IconButton>
|
||||
@@ -117,9 +108,3 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
</Base>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(BottomNavigation, (state) => {
|
||||
return {
|
||||
lastOpened: state.lastOpened,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Route, Switch } from "react-router";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
|
||||
|
||||
import SidebarBase from "./SidebarBase";
|
||||
import HomeSidebar from "./left/HomeSidebar";
|
||||
import ServerListSidebar from "./left/ServerListSidebar";
|
||||
import ServerSidebar from "./left/ServerSidebar";
|
||||
import { useSelector } from "react-redux";
|
||||
import { State } from "../../redux";
|
||||
|
||||
export default function LeftSidebar() {
|
||||
const isOpen = useSelector((state: State) => state.sectionToggle['sidebar_channels'] ?? true)
|
||||
const layout = useApplicationState().layout;
|
||||
const isOpen = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||
|
||||
return (
|
||||
<SidebarBase>
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
}
|
||||
|
||||
&[data-muted="true"] {
|
||||
color: var(--tertiary-foreground);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&[data-alert="true"],
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Notepad,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, Redirect, useLocation, useParams } from "react-router-dom";
|
||||
import { Link, useLocation, useParams } from "react-router-dom";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
@@ -15,54 +15,38 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import Category from "../../ui/Category";
|
||||
import placeholderSVG from "../items/placeholder.svg";
|
||||
import { mapChannelWithUnread, useUnreads } from "./common";
|
||||
|
||||
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
|
||||
import ButtonItem, { ChannelButton } from "../items/ButtonItem";
|
||||
import ConnectionStatus from "../items/ConnectionStatus";
|
||||
|
||||
type Props = {
|
||||
unreads: Unreads;
|
||||
};
|
||||
|
||||
const HomeSidebar = observer((props: Props) => {
|
||||
export default observer(() => {
|
||||
const { pathname } = useLocation();
|
||||
const client = useContext(AppContext);
|
||||
const { channel } = useParams<{ channel: string }>();
|
||||
const state = useApplicationState();
|
||||
const { channel: currentChannel } = useParams<{ channel: string }>();
|
||||
const { openScreen } = useIntermediate();
|
||||
|
||||
const channels = [...client.channels.values()]
|
||||
.filter(
|
||||
(x) =>
|
||||
x.channel_type === "DirectMessage" ||
|
||||
x.channel_type === "Group",
|
||||
)
|
||||
.map((x) => mapChannelWithUnread(x, props.unreads));
|
||||
const channels = [...client.channels.values()].filter(
|
||||
(x) => x.channel_type === "DirectMessage" || x.channel_type === "Group",
|
||||
);
|
||||
|
||||
const obj = client.channels.get(channel);
|
||||
if (channel && !obj) return <Redirect to="/" />;
|
||||
if (obj) useUnreads({ ...props, channel: obj });
|
||||
const obj = client.channels.get(currentChannel);
|
||||
|
||||
useEffect(() => {
|
||||
if (!channel) return;
|
||||
// ! FIXME: move this globally
|
||||
// Track what page the user was last on (in home page).
|
||||
useEffect(() => state.layout.setLastHomePath(pathname), [pathname]);
|
||||
|
||||
dispatch({
|
||||
type: "LAST_OPENED_SET",
|
||||
parent: "home",
|
||||
child: channel,
|
||||
});
|
||||
}, [channel]);
|
||||
|
||||
channels.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
|
||||
channels.sort((b, a) =>
|
||||
a.last_message_id_or_past.localeCompare(b.last_message_id_or_past),
|
||||
);
|
||||
|
||||
return (
|
||||
<GenericSidebarBase mobilePadding>
|
||||
@@ -132,31 +116,37 @@ const HomeSidebar = observer((props: Props) => {
|
||||
{channels.length === 0 && (
|
||||
<img src={placeholderSVG} loading="eager" />
|
||||
)}
|
||||
{channels.map((x) => {
|
||||
{channels.map((channel) => {
|
||||
let user;
|
||||
if (x.channel.channel_type === "DirectMessage") {
|
||||
if (!x.channel.active) return null;
|
||||
user = x.channel.recipient;
|
||||
if (channel.channel_type === "DirectMessage") {
|
||||
if (!channel.active) return null;
|
||||
user = channel.recipient;
|
||||
|
||||
if (!user) {
|
||||
console.warn(
|
||||
`Skipped DM ${x.channel._id} because user was missing.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (!user) return null;
|
||||
}
|
||||
|
||||
const isUnread = channel.isUnread(state.notifications);
|
||||
const mentionCount = channel.getMentions(
|
||||
state.notifications,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={x.channel._id}
|
||||
active={x.channel._id === channel}
|
||||
to={`/channel/${x.channel._id}`}>
|
||||
key={channel._id}
|
||||
active={channel._id === currentChannel}
|
||||
to={`/channel/${channel._id}`}>
|
||||
<ChannelButton
|
||||
user={user}
|
||||
channel={x.channel}
|
||||
alert={x.unread}
|
||||
alertCount={x.alertCount}
|
||||
active={x.channel._id === channel}
|
||||
channel={channel}
|
||||
alert={
|
||||
mentionCount > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
alertCount={mentionCount}
|
||||
active={channel._id === currentChannel}
|
||||
/>
|
||||
</ConditionalLink>
|
||||
);
|
||||
@@ -166,13 +156,3 @@ const HomeSidebar = observer((props: Props) => {
|
||||
</GenericSidebarBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(
|
||||
HomeSidebar,
|
||||
(state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
};
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -12,9 +12,7 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { LastOpened } from "../../../redux/reducers/last_opened";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
@@ -25,7 +23,6 @@ import UserHover from "../../common/user/UserHover";
|
||||
import UserIcon from "../../common/user/UserIcon";
|
||||
import IconButton from "../../ui/IconButton";
|
||||
import LineDivider from "../../ui/LineDivider";
|
||||
import { mapChannelWithUnread } from "./common";
|
||||
|
||||
import { Children } from "../../../types/Preact";
|
||||
|
||||
@@ -195,46 +192,14 @@ function Swoosh() {
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
unreads: Unreads;
|
||||
lastOpened: LastOpened;
|
||||
}
|
||||
|
||||
export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const state = useApplicationState();
|
||||
|
||||
const { server: server_id } = useParams<{ server?: string }>();
|
||||
const server = server_id ? client.servers.get(server_id) : undefined;
|
||||
const activeServers = [...client.servers.values()];
|
||||
const channels = [...client.channels.values()].map((x) =>
|
||||
mapChannelWithUnread(x, unreads),
|
||||
);
|
||||
|
||||
const unreadChannels = channels
|
||||
.filter((x) => x.unread)
|
||||
.map((x) => x.channel?._id);
|
||||
|
||||
const servers = activeServers.map((server) => {
|
||||
let alertCount = 0;
|
||||
for (const id of server.channel_ids) {
|
||||
const channel = channels.find((x) => x.channel?._id === id);
|
||||
if (channel?.alertCount) {
|
||||
alertCount += channel.alertCount;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
server,
|
||||
unread: (typeof server.channel_ids.find((x) =>
|
||||
unreadChannels.includes(x),
|
||||
) !== "undefined"
|
||||
? alertCount > 0
|
||||
? "mention"
|
||||
: "unread"
|
||||
: undefined) as "mention" | "unread" | undefined,
|
||||
alertCount,
|
||||
};
|
||||
});
|
||||
const servers = [...client.servers.values()];
|
||||
const channels = [...client.channels.values()];
|
||||
|
||||
const history = useHistory();
|
||||
const path = useLocation().pathname;
|
||||
@@ -242,16 +207,16 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
|
||||
let homeUnread: "mention" | "unread" | undefined;
|
||||
let alertCount = 0;
|
||||
for (const x of channels) {
|
||||
if (x.channel?.channel_type === "Group" && x.unread) {
|
||||
for (const channel of channels) {
|
||||
if (channel?.channel_type === "Group" && channel.unread) {
|
||||
homeUnread = "unread";
|
||||
alertCount += x.alertCount ?? 0;
|
||||
alertCount += channel.mentions.length;
|
||||
}
|
||||
|
||||
if (
|
||||
x.channel?.channel_type === "DirectMessage" &&
|
||||
x.channel.active &&
|
||||
x.unread
|
||||
channel.channel_type === "DirectMessage" &&
|
||||
channel.active &&
|
||||
channel.unread
|
||||
) {
|
||||
alertCount++;
|
||||
}
|
||||
@@ -270,7 +235,7 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
<ServerList>
|
||||
<ConditionalLink
|
||||
active={homeActive}
|
||||
to={lastOpened.home ? `/channel/${lastOpened.home}` : "/"}>
|
||||
to={state.layout.getLastHomePath()}>
|
||||
<ServerEntry home active={homeActive}>
|
||||
<Swoosh />
|
||||
<div
|
||||
@@ -278,13 +243,13 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
onClick={() =>
|
||||
homeActive && history.push("/settings")
|
||||
}>
|
||||
<UserHover user={client.user}>
|
||||
<UserHover user={client.user ?? undefined}>
|
||||
<Icon
|
||||
size={42}
|
||||
unread={homeUnread}
|
||||
count={alertCount}>
|
||||
<UserIcon
|
||||
target={client.user}
|
||||
target={client.user ?? undefined}
|
||||
size={32}
|
||||
status
|
||||
hover
|
||||
@@ -295,35 +260,40 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
</ServerEntry>
|
||||
</ConditionalLink>
|
||||
<LineDivider />
|
||||
{servers.map((entry) => {
|
||||
const active = entry.server._id === server?._id;
|
||||
const id = lastOpened[entry.server._id];
|
||||
{servers.map((server) => {
|
||||
const active = server._id === server_id;
|
||||
|
||||
const isUnread = server.isUnread(state.notifications);
|
||||
const mentionCount = server.getMentions(
|
||||
state.notifications,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={entry.server._id}
|
||||
key={server._id}
|
||||
active={active}
|
||||
to={`/server/${entry.server._id}${
|
||||
id ? `/channel/${id}` : ""
|
||||
}`}>
|
||||
to={state.layout.getServerPath(server._id)}>
|
||||
<ServerEntry
|
||||
active={active}
|
||||
onContextMenu={attachContextMenu("Menu", {
|
||||
server: entry.server._id,
|
||||
unread: entry.unread,
|
||||
server: server._id,
|
||||
unread: isUnread,
|
||||
})}>
|
||||
<Swoosh />
|
||||
<Tooltip
|
||||
content={entry.server.name}
|
||||
content={server.name}
|
||||
placement="right">
|
||||
<Icon
|
||||
size={42}
|
||||
unread={entry.unread}
|
||||
count={entry.alertCount}>
|
||||
<ServerIcon
|
||||
size={32}
|
||||
target={entry.server}
|
||||
/>
|
||||
unread={
|
||||
mentionCount > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
count={mentionCount}>
|
||||
<ServerIcon size={32} target={server} />
|
||||
</Icon>
|
||||
</Tooltip>
|
||||
</ServerEntry>
|
||||
@@ -357,10 +327,3 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
</ServersBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(ServerListSidebar, (state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
lastOpened: state.lastOpened,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,26 +10,17 @@ import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { internalEmit } from "../../../lib/eventEmitter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { Notifications } from "../../../redux/reducers/notifications";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import CollapsibleSection from "../../common/CollapsibleSection";
|
||||
import ServerHeader from "../../common/ServerHeader";
|
||||
import Category from "../../ui/Category";
|
||||
import { mapChannelWithUnread, useUnreads } from "./common";
|
||||
|
||||
import { ChannelButton } from "../items/ButtonItem";
|
||||
import ConnectionStatus from "../items/ConnectionStatus";
|
||||
|
||||
interface Props {
|
||||
unreads: Unreads;
|
||||
notifications: Notifications;
|
||||
}
|
||||
|
||||
const ServerBase = styled.div`
|
||||
height: 100%;
|
||||
width: 232px;
|
||||
@@ -57,8 +48,9 @@ const ServerList = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const ServerSidebar = observer((props: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const state = useApplicationState();
|
||||
const { server: server_id, channel: channel_id } =
|
||||
useParams<{ server: string; channel?: string }>();
|
||||
|
||||
@@ -76,16 +68,13 @@ const ServerSidebar = observer((props: Props) => {
|
||||
);
|
||||
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
|
||||
|
||||
if (channel) useUnreads({ ...props, channel });
|
||||
|
||||
// ! FIXME: move this globally
|
||||
// Track which channel the user was last on.
|
||||
useEffect(() => {
|
||||
if (!channel_id) return;
|
||||
if (!server_id) return;
|
||||
|
||||
dispatch({
|
||||
type: "LAST_OPENED_SET",
|
||||
parent: server_id!,
|
||||
child: channel_id!,
|
||||
});
|
||||
state.layout.setLastOpened(server_id, channel_id);
|
||||
}, [channel_id, server_id]);
|
||||
|
||||
const uncategorised = new Set(server.channel_ids);
|
||||
@@ -96,7 +85,8 @@ const ServerSidebar = observer((props: Props) => {
|
||||
if (!entry) return;
|
||||
|
||||
const active = channel?._id === entry._id;
|
||||
const muted = props.notifications[id] === "none";
|
||||
const isUnread = entry.isUnread(state.notifications);
|
||||
const mentionCount = entry.getMentions(state.notifications);
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
@@ -117,10 +107,15 @@ const ServerSidebar = observer((props: Props) => {
|
||||
<ChannelButton
|
||||
channel={entry}
|
||||
active={active}
|
||||
// ! FIXME: pull it out directly
|
||||
alert={mapChannelWithUnread(entry, props.unreads).unread}
|
||||
alert={
|
||||
mentionCount.length > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
compact
|
||||
muted={muted}
|
||||
muted={state.notifications.isMuted(entry)}
|
||||
/>
|
||||
</ConditionalLink>
|
||||
);
|
||||
@@ -163,10 +158,3 @@ const ServerSidebar = observer((props: Props) => {
|
||||
</ServerBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(ServerSidebar, (state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
notifications: state.notifications,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { reaction } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
|
||||
type UnreadProps = {
|
||||
channel: Channel;
|
||||
unreads: Unreads;
|
||||
};
|
||||
|
||||
export function useUnreads({ channel, unreads }: UnreadProps) {
|
||||
// const firstLoad = useRef(true);
|
||||
useLayoutEffect(() => {
|
||||
function checkUnread(target: Channel) {
|
||||
if (!target) return;
|
||||
if (target._id !== channel._id) return;
|
||||
if (
|
||||
target.channel_type === "SavedMessages" ||
|
||||
target.channel_type === "VoiceChannel"
|
||||
)
|
||||
return;
|
||||
|
||||
const unread = unreads[channel._id]?.last_id;
|
||||
if (target.last_message_id) {
|
||||
if (
|
||||
!unread ||
|
||||
(unread && target.last_message_id.localeCompare(unread) > 0)
|
||||
) {
|
||||
dispatch({
|
||||
type: "UNREADS_MARK_READ",
|
||||
channel: channel._id,
|
||||
message: target.last_message_id,
|
||||
});
|
||||
|
||||
channel.ack(target.last_message_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkUnread(channel);
|
||||
return reaction(
|
||||
() => channel.last_message,
|
||||
() => checkUnread(channel),
|
||||
);
|
||||
}, [channel, unreads]);
|
||||
}
|
||||
|
||||
export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
|
||||
const last_message_id = channel.last_message_id;
|
||||
|
||||
let unread: "mention" | "unread" | undefined;
|
||||
let alertCount: undefined | number;
|
||||
if (last_message_id && unreads) {
|
||||
const u = unreads[channel._id];
|
||||
if (u) {
|
||||
if (u.mentions && u.mentions.length > 0) {
|
||||
alertCount = u.mentions.length;
|
||||
unread = "mention";
|
||||
} else if (
|
||||
u.last_id &&
|
||||
(last_message_id as string).localeCompare(u.last_id) > 0
|
||||
) {
|
||||
unread = "unread";
|
||||
}
|
||||
} else {
|
||||
unread = "unread";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
channel,
|
||||
timestamp: last_message_id ?? channel._id,
|
||||
unread,
|
||||
alertCount,
|
||||
};
|
||||
}
|
||||
221
src/components/settings/AppearanceShims.tsx
Normal file
221
src/components/settings/AppearanceShims.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import { Store } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link } from "react-router-dom";
|
||||
// @ts-expect-error shade-blend-color does not have typings.
|
||||
import pSBC from "shade-blend-color";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import TextAreaAutoSize from "../../lib/TextAreaAutoSize";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import {
|
||||
Fonts,
|
||||
FONTS,
|
||||
FONT_KEYS,
|
||||
MonospaceFonts,
|
||||
MONOSPACE_FONTS,
|
||||
MONOSPACE_FONT_KEYS,
|
||||
} from "../../context/Theme";
|
||||
|
||||
import Checkbox from "../ui/Checkbox";
|
||||
import ColourSwatches from "../ui/ColourSwatches";
|
||||
import ComboBox from "../ui/ComboBox";
|
||||
import Radio from "../ui/Radio";
|
||||
import CategoryButton from "../ui/fluent/CategoryButton";
|
||||
|
||||
import { EmojiSelector } from "./appearance/EmojiSelector";
|
||||
import { ThemeBaseSelector } from "./appearance/ThemeBaseSelector";
|
||||
|
||||
/**
|
||||
* Component providing a way to switch the base theme being used.
|
||||
*/
|
||||
export const ThemeBaseSelectorShim = observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
return (
|
||||
<ThemeBaseSelector
|
||||
value={theme.isModified() ? undefined : theme.getBase()}
|
||||
setValue={(base) => {
|
||||
theme.setBase(base);
|
||||
theme.reset();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a link to the theme shop.
|
||||
* Only appears if experiment is enabled.
|
||||
* TODO: stabilise
|
||||
*/
|
||||
export const ThemeShopShim = () => {
|
||||
if (!useApplicationState().experiments.isEnabled("theme_shop")) return null;
|
||||
|
||||
return (
|
||||
<Link to="/settings/theme_shop" replace>
|
||||
<CategoryButton icon={<Store size={24} />} action="chevron" hover>
|
||||
<Text id="app.settings.pages.theme_shop.title" />
|
||||
</CategoryButton>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Component providing a way to change current accent colour.
|
||||
*/
|
||||
export const ThemeAccentShim = observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.accent_selector" />
|
||||
</h3>
|
||||
<ColourSwatches
|
||||
value={theme.getVariable("accent")}
|
||||
onChange={(colour) => {
|
||||
theme.setVariable("accent", colour as string);
|
||||
theme.setVariable("scrollbar-thumb", pSBC(-0.2, colour));
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a way to edit custom CSS.
|
||||
*/
|
||||
export const ThemeCustomCSSShim = observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.custom_css" />
|
||||
</h3>
|
||||
<TextAreaAutoSize
|
||||
maxRows={20}
|
||||
minHeight={480}
|
||||
code
|
||||
value={theme.getCSS() ?? ""}
|
||||
onChange={(ev) => theme.setCSS(ev.currentTarget.value)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a way to switch between compact and normal message view.
|
||||
*/
|
||||
export const DisplayCompactShim = () => {
|
||||
// TODO: WIP feature
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Component providing a way to change primary text font.
|
||||
*/
|
||||
export const DisplayFontShim = observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.getFont()}
|
||||
onChange={(e) => theme.setFont(e.currentTarget.value as Fonts)}>
|
||||
{FONT_KEYS.map((key) => (
|
||||
<option value={key} key={key}>
|
||||
{FONTS[key as keyof typeof FONTS].name}
|
||||
</option>
|
||||
))}
|
||||
</ComboBox>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a way to change secondary, monospace text font.
|
||||
*/
|
||||
export const DisplayMonospaceFontShim = observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.mono_font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.getMonospaceFont()}
|
||||
onChange={(e) =>
|
||||
theme.setMonospaceFont(
|
||||
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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a way to toggle font ligatures.
|
||||
*/
|
||||
export const DisplayLigaturesShim = observer(() => {
|
||||
const settings = useApplicationState().settings;
|
||||
if (settings.theme.getFont() !== "Inter") return null;
|
||||
|
||||
return (
|
||||
<p>
|
||||
<Checkbox
|
||||
checked={settings.get("appearance:ligatures") ?? false}
|
||||
onChange={(v) => settings.set("appearance:ligatures", v)}
|
||||
description={
|
||||
<Text id="app.settings.pages.appearance.ligatures_desc" />
|
||||
}>
|
||||
<Text id="app.settings.pages.appearance.ligatures" />
|
||||
</Checkbox>
|
||||
</p>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Component providing a way to change emoji pack.
|
||||
*/
|
||||
export const DisplayEmojiShim = observer(() => {
|
||||
const settings = useApplicationState().settings;
|
||||
return (
|
||||
<EmojiSelector
|
||||
value={settings.get("appearance:emoji")}
|
||||
setValue={(v) => settings.set("appearance:emoji", v)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
161
src/components/settings/appearance/EmojiSelector.tsx
Normal file
161
src/components/settings/appearance/EmojiSelector.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { EmojiPack } from "../../common/Emoji";
|
||||
import mutantSVG from "./mutant_emoji.svg";
|
||||
import notoSVG from "./noto_emoji.svg";
|
||||
import openmojiSVG from "./openmoji_emoji.svg";
|
||||
import twemojiSVG from "./twemoji_emoji.svg";
|
||||
|
||||
const Container = styled.div`
|
||||
gap: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.row {
|
||||
gap: 12px;
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 2rem 1.2rem;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
cursor: pointer;
|
||||
transition: border 0.3s;
|
||||
background: var(--hover);
|
||||
border: 3px solid transparent;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&[data-active="true"] {
|
||||
cursor: default;
|
||||
background: var(--secondary-background);
|
||||
border: 3px solid var(--accent);
|
||||
|
||||
&:hover {
|
||||
border: 3px solid var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--secondary-background);
|
||||
border: 3px solid var(--tertiary-background);
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: unset;
|
||||
|
||||
a {
|
||||
opacity: 0.7;
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
value?: EmojiPack;
|
||||
setValue: (pack: EmojiPack) => void;
|
||||
}
|
||||
|
||||
export function EmojiSelector({ value, setValue }: Props) {
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.emoji_pack" />
|
||||
</h3>
|
||||
<Container>
|
||||
<div class="row">
|
||||
<div>
|
||||
<div
|
||||
class="button"
|
||||
onClick={() => setValue("mutant")}
|
||||
data-active={!value || value === "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
|
||||
class="button"
|
||||
onClick={() => setValue("twemoji")}
|
||||
data-active={value === "twemoji"}>
|
||||
<img
|
||||
loading="eager"
|
||||
src={twemojiSVG}
|
||||
draggable={false}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
<h4>Twemoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<div
|
||||
class="button"
|
||||
onClick={() => setValue("openmoji")}
|
||||
data-active={value === "openmoji"}>
|
||||
<img
|
||||
loading="eager"
|
||||
src={openmojiSVG}
|
||||
draggable={false}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
<h4>Openmoji</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="button"
|
||||
onClick={() => setValue("noto")}
|
||||
data-active={value === "noto"}>
|
||||
<img
|
||||
loading="eager"
|
||||
src={notoSVG}
|
||||
draggable={false}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
<h4>Noto Emoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
84
src/components/settings/appearance/ThemeBaseSelector.tsx
Normal file
84
src/components/settings/appearance/ThemeBaseSelector.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import darkSVG from "./dark.svg";
|
||||
import lightSVG from "./light.svg";
|
||||
|
||||
const List = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
border-radius: var(--border-radius);
|
||||
transition: border 0.3s;
|
||||
border: 3px solid transparent;
|
||||
width: 100%;
|
||||
|
||||
&[data-active="true"] {
|
||||
cursor: default;
|
||||
border: 3px solid var(--accent);
|
||||
&:hover {
|
||||
border: 3px solid var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 3px solid var(--tertiary-background);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
value?: "light" | "dark";
|
||||
setValue: (base: "light" | "dark") => void;
|
||||
}
|
||||
|
||||
export function ThemeBaseSelector({ value, setValue }: Props) {
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.theme" />
|
||||
</h3>
|
||||
<List>
|
||||
<div>
|
||||
<img
|
||||
loading="eager"
|
||||
src={lightSVG}
|
||||
draggable={false}
|
||||
data-active={value === "light"}
|
||||
onClick={() => setValue("light")}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.light" />
|
||||
</h4>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
loading="eager"
|
||||
src={darkSVG}
|
||||
draggable={false}
|
||||
data-active={value === "dark"}
|
||||
onClick={() => setValue("dark")}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.dark" />
|
||||
</h4>
|
||||
</div>
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
}
|
||||
181
src/components/settings/appearance/ThemeOverrides.tsx
Normal file
181
src/components/settings/appearance/ThemeOverrides.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { Pencil } from "@styled-icons/boxicons-regular";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useDebounceCallback } from "../../../lib/debounce";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { Variables } from "../../../context/Theme";
|
||||
|
||||
import InputBox from "../../ui/InputBox";
|
||||
|
||||
const Container = styled.div`
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default observer(() => {
|
||||
const theme = useApplicationState().settings.theme;
|
||||
const setVariable = useDebounceCallback(
|
||||
(data) => {
|
||||
const { key, value } = data as { key: Variables; value: string };
|
||||
theme.setVariable(key, value);
|
||||
},
|
||||
[theme],
|
||||
100,
|
||||
);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{(
|
||||
[
|
||||
"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((key) => (
|
||||
<div
|
||||
class="entry"
|
||||
key={key}
|
||||
style={{ backgroundColor: theme.getVariable(key) }}>
|
||||
<div class="input">
|
||||
<input
|
||||
type="color"
|
||||
value={theme.getVariable(key)}
|
||||
onChange={(el) =>
|
||||
setVariable({
|
||||
key,
|
||||
value: el.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
color: getContrastingColour(
|
||||
theme.getVariable(key),
|
||||
theme.getVariable("primary-background"),
|
||||
),
|
||||
}}>
|
||||
{key}
|
||||
</span>
|
||||
<div class="override">
|
||||
<div
|
||||
class="picker"
|
||||
onClick={(e) =>
|
||||
e.currentTarget.parentElement?.parentElement
|
||||
?.querySelector("input")
|
||||
?.click()
|
||||
}>
|
||||
<Pencil size={24} />
|
||||
</div>
|
||||
<InputBox
|
||||
type="text"
|
||||
class="text"
|
||||
value={theme.getVariable(key)}
|
||||
onChange={(el) =>
|
||||
setVariable({
|
||||
key,
|
||||
value: el.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
|
||||
function getContrastingColour(hex: string, fallback: string): string {
|
||||
hex = hex.replace("#", "");
|
||||
const r = parseInt(hex.substr(0, 2), 16);
|
||||
const g = parseInt(hex.substr(2, 2), 16);
|
||||
const b = parseInt(hex.substr(4, 2), 16);
|
||||
const cc = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(cc))
|
||||
return getContrastingColour(fallback, "#fffff");
|
||||
return cc >= 175 ? "black" : "white";
|
||||
}
|
||||
89
src/components/settings/appearance/ThemeTools.tsx
Normal file
89
src/components/settings/appearance/ThemeTools.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Import, Reset } from "@styled-icons/boxicons-regular";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
|
||||
import Tooltip from "../../common/Tooltip";
|
||||
import Button from "../../ui/Button";
|
||||
|
||||
const Actions = styled.div`
|
||||
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;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ThemeTools() {
|
||||
const { writeClipboard, openScreen } = useIntermediate();
|
||||
const theme = useApplicationState().settings.theme;
|
||||
|
||||
return (
|
||||
<Actions>
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.settings.pages.appearance.reset_overrides" />
|
||||
}>
|
||||
<Button contrast iconbutton onClick={theme.reset}>
|
||||
<Reset size={22} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<div
|
||||
class="code"
|
||||
onClick={() => writeClipboard(JSON.stringify(theme))}>
|
||||
<Tooltip content={<Text id="app.special.copy" />}>
|
||||
{" "}
|
||||
{JSON.stringify(theme)}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip
|
||||
content={<Text id="app.settings.pages.appearance.import" />}>
|
||||
<Button
|
||||
contrast
|
||||
iconbutton
|
||||
onClick={async () => {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
theme.hydrate(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 (text) =>
|
||||
theme.hydrate(JSON.parse(text)),
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<Import size={22} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Actions>
|
||||
);
|
||||
}
|
||||
153
src/components/settings/appearance/dark.svg
Normal file
153
src/components/settings/appearance/dark.svg
Normal file
@@ -0,0 +1,153 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 14 KiB |
153
src/components/settings/appearance/light.svg
Normal file
153
src/components/settings/appearance/light.svg
Normal file
@@ -0,0 +1,153 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 14 KiB |
1
src/components/settings/appearance/mutant_emoji.svg
Normal file
1
src/components/settings/appearance/mutant_emoji.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
1
src/components/settings/appearance/noto_emoji.svg
Normal file
1
src/components/settings/appearance/noto_emoji.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
1
src/components/settings/appearance/openmoji_emoji.svg
Normal file
1
src/components/settings/appearance/openmoji_emoji.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
1
src/components/settings/appearance/twemoji_emoji.svg
Normal file
1
src/components/settings/appearance/twemoji_emoji.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.7 KiB |
@@ -5,6 +5,8 @@ import styled, { css } from "styled-components";
|
||||
import { RefObject } from "preact";
|
||||
import { useRef } from "preact/hooks";
|
||||
|
||||
import { useDebounceCallback } from "../../lib/debounce";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
@@ -115,6 +117,11 @@ const Rows = styled.div`
|
||||
|
||||
export default function ColourSwatches({ value, onChange }: Props) {
|
||||
const ref = useRef<HTMLInputElement>() as RefObject<HTMLInputElement>;
|
||||
const setValue = useDebounceCallback(
|
||||
(value) => onChange(value as string),
|
||||
[onChange],
|
||||
100,
|
||||
);
|
||||
|
||||
return (
|
||||
<SwatchesBase>
|
||||
@@ -122,7 +129,7 @@ export default function ColourSwatches({ value, onChange }: Props) {
|
||||
type="color"
|
||||
value={value}
|
||||
ref={ref}
|
||||
onChange={(ev) => onChange(ev.currentTarget.value)}
|
||||
onChange={(ev) => setValue(ev.currentTarget.value)}
|
||||
/>
|
||||
<Swatch
|
||||
colour={value}
|
||||
|
||||
@@ -167,7 +167,7 @@ export default function Modal(props: Props) {
|
||||
isModalClosing = animateClose;
|
||||
const onClose = useCallback(() => {
|
||||
setAnimateClose(true);
|
||||
setTimeout(() => props.onClose?.(), 2e2);
|
||||
setTimeout(() => props.onClose!(), 2e2);
|
||||
}, [setAnimateClose, props]);
|
||||
|
||||
useEffect(() => internalSubscribe("Modal", "close", onClose), [onClose]);
|
||||
|
||||
@@ -7,9 +7,9 @@ interface Props {
|
||||
children: Children;
|
||||
description?: Children;
|
||||
|
||||
checked: boolean;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
onSelect: () => void;
|
||||
onSelect?: () => void;
|
||||
}
|
||||
|
||||
interface BaseProps {
|
||||
@@ -87,9 +87,10 @@ const RadioDescription = styled.span<BaseProps>`
|
||||
`;
|
||||
|
||||
export default function Radio(props: Props) {
|
||||
const selected = props.checked ?? false;
|
||||
return (
|
||||
<RadioBase
|
||||
selected={props.checked}
|
||||
selected={selected}
|
||||
disabled={props.disabled}
|
||||
onClick={() =>
|
||||
!props.disabled && props.onSelect && props.onSelect()
|
||||
@@ -101,7 +102,7 @@ export default function Radio(props: Props) {
|
||||
<span>
|
||||
<span>{props.children}</span>
|
||||
{props.description && (
|
||||
<RadioDescription selected={props.checked}>
|
||||
<RadioDescription selected={selected}>
|
||||
{props.description}
|
||||
</RadioDescription>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user