From b5a11d5c8fcfa68fcbc339737e3ed6170e79ebff Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Mon, 5 Jul 2021 11:25:20 +0100
Subject: [PATCH] Use tabWidth 4 without actual tabs.

---
 .prettierrc.js                                |    1 -
 src/assets/emojis.ts                          | 3696 ++++++++---------
 src/assets/sounds/Audio.ts                    |   30 +-
 src/components/common/AutoComplete.tsx        |  888 ++--
 src/components/common/ChannelIcon.tsx         |   92 +-
 src/components/common/CollapsibleSection.tsx  |   84 +-
 src/components/common/Emoji.tsx               |   78 +-
 src/components/common/IconBase.tsx            |   52 +-
 src/components/common/LocaleSelector.tsx      |   46 +-
 src/components/common/ServerHeader.tsx        |   58 +-
 src/components/common/ServerIcon.tsx          |   90 +-
 src/components/common/Tooltip.tsx             |   78 +-
 src/components/common/UpdateIndicator.tsx     |   22 +-
 src/components/common/messaging/Message.tsx   |  196 +-
 .../common/messaging/MessageBase.tsx          |  344 +-
 .../common/messaging/MessageBox.tsx           |  868 ++--
 .../common/messaging/SystemMessage.tsx        |  268 +-
 .../messaging/attachments/Attachment.tsx      |  220 +-
 .../attachments/AttachmentActions.tsx         |  186 +-
 .../messaging/attachments/MessageReply.tsx    |  126 +-
 .../common/messaging/attachments/TextFile.tsx |   98 +-
 .../common/messaging/bars/FilePreview.tsx     |  384 +-
 .../common/messaging/bars/JumpToBottom.tsx    |   92 +-
 .../common/messaging/bars/ReplyBar.tsx        |  222 +-
 .../common/messaging/bars/TypingIndicator.tsx |  196 +-
 .../common/messaging/embed/Embed.tsx          |  292 +-
 .../common/messaging/embed/EmbedMedia.tsx     |  172 +-
 .../messaging/embed/EmbedMediaActions.tsx     |   34 +-
 src/components/common/user/UserCheckbox.tsx   |   12 +-
 src/components/common/user/UserHeader.tsx     |  104 +-
 src/components/common/user/UserIcon.tsx       |  144 +-
 src/components/common/user/UserShort.tsx      |   34 +-
 src/components/common/user/UserStatus.tsx     |   34 +-
 src/components/markdown/Markdown.tsx          |   16 +-
 src/components/markdown/Renderer.tsx          |  266 +-
 .../navigation/BottomNavigation.tsx           |  124 +-
 src/components/navigation/LeftSidebar.tsx     |   46 +-
 src/components/navigation/RightSidebar.tsx    |   24 +-
 src/components/navigation/SidebarBase.tsx     |   48 +-
 .../navigation/items/ButtonItem.tsx           |  362 +-
 .../navigation/items/ConnectionStatus.tsx     |   58 +-
 .../navigation/left/HomeSidebar.tsx           |  308 +-
 .../navigation/left/ServerListSidebar.tsx     |  496 +--
 .../navigation/left/ServerSidebar.tsx         |  220 +-
 src/components/navigation/left/common.ts      |  156 +-
 .../navigation/right/ChannelDebugInfo.tsx     |   64 +-
 .../navigation/right/MemberSidebar.tsx        |  362 +-
 src/components/ui/Banner.tsx                  |   10 +-
 src/components/ui/Button.tsx                  |  102 +-
 src/components/ui/Category.tsx                |   82 +-
 src/components/ui/Checkbox.tsx                |  156 +-
 src/components/ui/ColourSwatches.tsx          |  192 +-
 src/components/ui/ComboBox.tsx                |   30 +-
 src/components/ui/DateDivider.tsx             |   64 +-
 src/components/ui/Details.tsx                 |  118 +-
 src/components/ui/Header.tsx                  |   84 +-
 src/components/ui/IconButton.tsx              |   72 +-
 src/components/ui/InputBox.tsx                |   64 +-
 src/components/ui/LineDivider.tsx             |   10 +-
 src/components/ui/Masks.tsx                   |   36 +-
 src/components/ui/Modal.tsx                   |  304 +-
 src/components/ui/Overline.tsx                |   88 +-
 src/components/ui/Preloader.tsx               |  146 +-
 src/components/ui/Radio.tsx                   |  182 +-
 src/components/ui/TextArea.tsx                |   88 +-
 src/components/ui/Tip.tsx                     |   96 +-
 src/context/Locale.tsx                        |  366 +-
 src/context/Settings.tsx                      |   60 +-
 src/context/Theme.tsx                         |  578 +--
 src/context/Voice.tsx                         |  356 +-
 src/context/index.tsx                         |   34 +-
 src/context/intermediate/Intermediate.tsx     |  298 +-
 src/context/intermediate/Modals.tsx           |   36 +-
 src/context/intermediate/Popovers.tsx         |   44 +-
 src/context/intermediate/modals/Clipboard.tsx |   46 +-
 src/context/intermediate/modals/Error.tsx     |   42 +-
 src/context/intermediate/modals/Input.tsx     |  292 +-
 .../intermediate/modals/Onboarding.tsx        |  110 +-
 src/context/intermediate/modals/Prompt.tsx    |  844 ++--
 src/context/intermediate/modals/SignedOut.tsx |   30 +-
 .../intermediate/popovers/ChannelInfo.tsx     |   54 +-
 .../intermediate/popovers/ImageViewer.tsx     |   56 +-
 .../intermediate/popovers/ModifyAccount.tsx   |  222 +-
 .../intermediate/popovers/PendingRequests.tsx |   34 +-
 .../intermediate/popovers/UserPicker.tsx      |  100 +-
 .../intermediate/popovers/UserProfile.tsx     |  626 +--
 src/context/revoltjs/CheckAuth.tsx            |   18 +-
 src/context/revoltjs/FileUploads.tsx          |  516 +--
 src/context/revoltjs/Notifications.tsx        |  462 +--
 src/context/revoltjs/RequiresOnline.tsx       |   56 +-
 src/context/revoltjs/RevoltClient.tsx         |  396 +-
 src/context/revoltjs/StateMonitor.tsx         |   90 +-
 src/context/revoltjs/SyncManager.tsx          |  226 +-
 src/context/revoltjs/events.ts                |  274 +-
 src/context/revoltjs/hooks.ts                 |  334 +-
 src/context/revoltjs/util.tsx                 |   80 +-
 src/env.d.ts                                  |    4 +-
 src/lib/ConditionalLink.tsx                   |   18 +-
 src/lib/ContextMenus.tsx                      | 1942 ++++-----
 src/lib/PaintCounter.tsx                      |   26 +-
 src/lib/TextAreaAutoSize.tsx                  |  198 +-
 src/lib/conversion.ts                         |   12 +-
 src/lib/debounce.ts                           |   26 +-
 src/lib/eventEmitter.ts                       |   12 +-
 src/lib/fileSize.ts                           |   12 +-
 src/lib/i18n.tsx                              |   74 +-
 src/lib/isTouchscreenDevice.ts                |   10 +-
 src/lib/renderer/Singleton.ts                 |  372 +-
 src/lib/renderer/simple/SimpleRenderer.ts     |  352 +-
 src/lib/renderer/types.ts                     |   68 +-
 src/lib/stopPropagation.ts                    |   10 +-
 src/lib/vortex/Signaling.ts                   |  350 +-
 src/lib/vortex/Types.ts                       |  120 +-
 src/lib/vortex/VoiceClient.ts                 |  626 +--
 src/lib/windowSize.ts                         |   32 +-
 src/main.tsx                                  |   14 +-
 src/pages/Open.tsx                            |  116 +-
 src/pages/RevoltApp.tsx                       |  172 +-
 src/pages/app.tsx                             |   40 +-
 src/pages/channels/Channel.tsx                |  220 +-
 src/pages/channels/ChannelHeader.tsx          |  206 +-
 src/pages/channels/actions/HeaderActions.tsx  |  168 +-
 .../channels/messaging/ConversationStart.tsx  |   50 +-
 src/pages/channels/messaging/MessageArea.tsx  |  450 +-
 .../channels/messaging/MessageEditor.tsx      |  220 +-
 .../channels/messaging/MessageRenderer.tsx    |  354 +-
 src/pages/channels/voice/VoiceHeader.tsx      |  228 +-
 src/pages/developer/Developer.tsx             |   54 +-
 src/pages/friends/Friend.tsx                  |  200 +-
 src/pages/friends/Friends.tsx                 |  338 +-
 src/pages/home/Home.tsx                       |   60 +-
 src/pages/invite/Invite.tsx                   |  202 +-
 src/pages/login/FormField.tsx                 |  120 +-
 src/pages/login/Login.tsx                     |  102 +-
 src/pages/login/forms/CaptchaBlock.tsx        |   52 +-
 src/pages/login/forms/Form.tsx                |  436 +-
 src/pages/login/forms/FormCreate.tsx          |   18 +-
 src/pages/login/forms/FormLogin.tsx           |   38 +-
 src/pages/login/forms/FormResend.tsx          |   18 +-
 src/pages/login/forms/FormReset.tsx           |   48 +-
 src/pages/login/forms/Legal.tsx               |   30 +-
 src/pages/login/forms/MailProvider.tsx        |   80 +-
 src/pages/settings/ChannelSettings.tsx        |  134 +-
 src/pages/settings/GenericSettings.tsx        |  256 +-
 src/pages/settings/ServerSettings.tsx         |  166 +-
 src/pages/settings/Settings.tsx               |  332 +-
 src/pages/settings/channel/Overview.tsx       |  184 +-
 src/pages/settings/channel/Permissions.tsx    |  164 +-
 src/pages/settings/panes/Account.tsx          |  158 +-
 src/pages/settings/panes/Appearance.tsx       |  600 +--
 src/pages/settings/panes/Experiments.tsx      |   74 +-
 src/pages/settings/panes/Feedback.tsx         |  186 +-
 src/pages/settings/panes/Languages.tsx        |  128 +-
 src/pages/settings/panes/Notifications.tsx    |  242 +-
 src/pages/settings/panes/Profile.tsx          |  318 +-
 src/pages/settings/panes/Sessions.tsx         |  322 +-
 src/pages/settings/panes/Sync.tsx             |   78 +-
 src/pages/settings/server/Bans.tsx            |   44 +-
 src/pages/settings/server/Invites.tsx         |  120 +-
 src/pages/settings/server/Members.tsx         |   56 +-
 src/pages/settings/server/Overview.tsx        |  324 +-
 src/pages/settings/server/Roles.tsx           |  286 +-
 src/redux/State.tsx                           |   24 +-
 src/redux/connector.tsx                       |   10 +-
 src/redux/index.ts                            |   98 +-
 src/redux/reducers/auth.ts                    |   76 +-
 src/redux/reducers/drafts.ts                  |   58 +-
 src/redux/reducers/experiments.ts             |   66 +-
 src/redux/reducers/index.ts                   |   68 +-
 src/redux/reducers/last_opened.ts             |   48 +-
 src/redux/reducers/locale.ts                  |   82 +-
 src/redux/reducers/notifications.ts           |  110 +-
 src/redux/reducers/queue.ts                   |  188 +-
 src/redux/reducers/section_toggle.ts          |   64 +-
 src/redux/reducers/server_config.ts           |   26 +-
 src/redux/reducers/settings.ts                |  164 +-
 src/redux/reducers/sync.ts                    |  138 +-
 src/redux/reducers/typing.ts                  |   82 +-
 src/redux/reducers/unreads.ts                 |  100 +-
 src/sw.ts                                     |  282 +-
 180 files changed, 17273 insertions(+), 17276 deletions(-)

diff --git a/.prettierrc.js b/.prettierrc.js
index ac8ca35..2d694ad 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,6 +1,5 @@
 module.exports = {
     "tabWidth": 4,
-    "useTabs": true,
     "trailingComma": "all",
     "jsxBracketSameLine": true,
     "importOrder": ["preact|classnames|.scss$", "/(lib)", "/(redux)", "/(context)", "/(ui|common)|.svg$", "^[./]"],
diff --git a/src/assets/emojis.ts b/src/assets/emojis.ts
index 1f58692..8c01f03 100644
--- a/src/assets/emojis.ts
+++ b/src/assets/emojis.ts
@@ -1,1850 +1,1850 @@
 export const emojiDictionary = {
-	"100": "💯",
-	"1234": "🔢",
-	grinning: "😀",
-	smiley: "😃",
-	smile: "😄",
-	grin: "😁",
-	laughing: "😆",
-	satisfied: "😆",
-	sweat_smile: "😅",
-	rofl: "🤣",
-	joy: "😂",
-	slightly_smiling_face: "🙂",
-	upside_down_face: "🙃",
-	wink: "😉",
-	blush: "😊",
-	innocent: "😇",
-	smiling_face_with_three_hearts: "🥰",
-	heart_eyes: "😍",
-	star_struck: "🤩",
-	kissing_heart: "😘",
-	kissing: "😗",
-	relaxed: "☺️",
-	kissing_closed_eyes: "😚",
-	kissing_smiling_eyes: "😙",
-	smiling_face_with_tear: "🥲",
-	yum: "😋",
-	stuck_out_tongue: "😛",
-	stuck_out_tongue_winking_eye: "😜",
-	zany_face: "🤪",
-	stuck_out_tongue_closed_eyes: "😝",
-	money_mouth_face: "🤑",
-	hugs: "🤗",
-	hand_over_mouth: "🤭",
-	shushing_face: "🤫",
-	thinking: "🤔",
-	zipper_mouth_face: "🤐",
-	raised_eyebrow: "🤨",
-	neutral_face: "😐",
-	expressionless: "😑",
-	no_mouth: "😶",
-	smirk: "😏",
-	unamused: "😒",
-	roll_eyes: "🙄",
-	grimacing: "😬",
-	lying_face: "🤥",
-	relieved: "😌",
-	pensive: "😔",
-	sleepy: "😪",
-	drooling_face: "🤤",
-	sleeping: "😴",
-	mask: "😷",
-	face_with_thermometer: "🤒",
-	face_with_head_bandage: "🤕",
-	nauseated_face: "🤢",
-	vomiting_face: "🤮",
-	sneezing_face: "🤧",
-	hot_face: "🥵",
-	cold_face: "🥶",
-	woozy_face: "🥴",
-	dizzy_face: "😵",
-	exploding_head: "🤯",
-	cowboy_hat_face: "🤠",
-	partying_face: "🥳",
-	disguised_face: "🥸",
-	sunglasses: "😎",
-	nerd_face: "🤓",
-	monocle_face: "🧐",
-	confused: "😕",
-	worried: "😟",
-	slightly_frowning_face: "🙁",
-	frowning_face: "☹️",
-	open_mouth: "😮",
-	hushed: "😯",
-	astonished: "😲",
-	flushed: "😳",
-	pleading_face: "🥺",
-	frowning: "😦",
-	anguished: "😧",
-	fearful: "😨",
-	cold_sweat: "😰",
-	disappointed_relieved: "😥",
-	cry: "😢",
-	sob: "😭",
-	scream: "😱",
-	confounded: "😖",
-	persevere: "😣",
-	disappointed: "😞",
-	sweat: "😓",
-	weary: "😩",
-	tired_face: "😫",
-	yawning_face: "🥱",
-	triumph: "😤",
-	rage: "😡",
-	pout: "😡",
-	angry: "😠",
-	cursing_face: "🤬",
-	smiling_imp: "😈",
-	imp: "👿",
-	skull: "💀",
-	skull_and_crossbones: "☠️",
-	hankey: "💩",
-	poop: "💩",
-	shit: "💩",
-	clown_face: "🤡",
-	japanese_ogre: "👹",
-	japanese_goblin: "👺",
-	ghost: "👻",
-	alien: "👽",
-	space_invader: "👾",
-	robot: "🤖",
-	smiley_cat: "😺",
-	smile_cat: "😸",
-	joy_cat: "😹",
-	heart_eyes_cat: "😻",
-	smirk_cat: "😼",
-	kissing_cat: "😽",
-	scream_cat: "🙀",
-	crying_cat_face: "😿",
-	pouting_cat: "😾",
-	see_no_evil: "🙈",
-	hear_no_evil: "🙉",
-	speak_no_evil: "🙊",
-	kiss: "💋",
-	love_letter: "💌",
-	cupid: "💘",
-	gift_heart: "💝",
-	sparkling_heart: "💖",
-	heartpulse: "💗",
-	heartbeat: "💓",
-	revolving_hearts: "💞",
-	two_hearts: "💕",
-	heart_decoration: "💟",
-	heavy_heart_exclamation: "❣️",
-	broken_heart: "💔",
-	heart: "❤️",
-	orange_heart: "🧡",
-	yellow_heart: "💛",
-	green_heart: "💚",
-	blue_heart: "💙",
-	purple_heart: "💜",
-	brown_heart: "🤎",
-	black_heart: "🖤",
-	white_heart: "🤍",
-	anger: "💢",
-	boom: "💥",
-	collision: "💥",
-	dizzy: "💫",
-	sweat_drops: "💦",
-	dash: "💨",
-	hole: "🕳️",
-	bomb: "💣",
-	speech_balloon: "💬",
-	eye_speech_bubble: "👁️‍🗨️",
-	left_speech_bubble: "🗨️",
-	right_anger_bubble: "🗯️",
-	thought_balloon: "💭",
-	zzz: "💤",
-	wave: "👋",
-	raised_back_of_hand: "🤚",
-	raised_hand_with_fingers_splayed: "🖐️",
-	hand: "✋",
-	raised_hand: "✋",
-	vulcan_salute: "🖖",
-	ok_hand: "👌",
-	pinched_fingers: "🤌",
-	pinching_hand: "🤏",
-	v: "✌️",
-	crossed_fingers: "🤞",
-	love_you_gesture: "🤟",
-	metal: "🤘",
-	call_me_hand: "🤙",
-	point_left: "👈",
-	point_right: "👉",
-	point_up_2: "👆",
-	middle_finger: "🖕",
-	fu: "🖕",
-	point_down: "👇",
-	point_up: "☝️",
-	"+1": "👍",
-	thumbsup: "👍",
-	"-1": "👎",
-	thumbsdown: "👎",
-	fist_raised: "✊",
-	fist: "✊",
-	fist_oncoming: "👊",
-	facepunch: "👊",
-	punch: "👊",
-	fist_left: "🤛",
-	fist_right: "🤜",
-	clap: "👏",
-	raised_hands: "🙌",
-	open_hands: "👐",
-	palms_up_together: "🤲",
-	handshake: "🤝",
-	pray: "🙏",
-	writing_hand: "✍️",
-	nail_care: "💅",
-	selfie: "🤳",
-	muscle: "💪",
-	mechanical_arm: "🦾",
-	mechanical_leg: "🦿",
-	leg: "🦵",
-	foot: "🦶",
-	ear: "👂",
-	ear_with_hearing_aid: "🦻",
-	nose: "👃",
-	brain: "🧠",
-	anatomical_heart: "🫀",
-	lungs: "🫁",
-	tooth: "🦷",
-	bone: "🦴",
-	eyes: "👀",
-	eye: "👁️",
-	tongue: "👅",
-	lips: "👄",
-	baby: "👶",
-	child: "🧒",
-	boy: "👦",
-	girl: "👧",
-	adult: "🧑",
-	blond_haired_person: "👱",
-	man: "👨",
-	bearded_person: "🧔",
-	red_haired_man: "👨‍🦰",
-	curly_haired_man: "👨‍🦱",
-	white_haired_man: "👨‍🦳",
-	bald_man: "👨‍🦲",
-	woman: "👩",
-	red_haired_woman: "👩‍🦰",
-	person_red_hair: "🧑‍🦰",
-	curly_haired_woman: "👩‍🦱",
-	person_curly_hair: "🧑‍🦱",
-	white_haired_woman: "👩‍🦳",
-	person_white_hair: "🧑‍🦳",
-	bald_woman: "👩‍🦲",
-	person_bald: "🧑‍🦲",
-	blond_haired_woman: "👱‍♀️",
-	blonde_woman: "👱‍♀️",
-	blond_haired_man: "👱‍♂️",
-	older_adult: "🧓",
-	older_man: "👴",
-	older_woman: "👵",
-	frowning_person: "🙍",
-	frowning_man: "🙍‍♂️",
-	frowning_woman: "🙍‍♀️",
-	pouting_face: "🙎",
-	pouting_man: "🙎‍♂️",
-	pouting_woman: "🙎‍♀️",
-	no_good: "🙅",
-	no_good_man: "🙅‍♂️",
-	ng_man: "🙅‍♂️",
-	no_good_woman: "🙅‍♀️",
-	ng_woman: "🙅‍♀️",
-	ok_person: "🙆",
-	ok_man: "🙆‍♂️",
-	ok_woman: "🙆‍♀️",
-	tipping_hand_person: "💁",
-	information_desk_person: "💁",
-	tipping_hand_man: "💁‍♂️",
-	sassy_man: "💁‍♂️",
-	tipping_hand_woman: "💁‍♀️",
-	sassy_woman: "💁‍♀️",
-	raising_hand: "🙋",
-	raising_hand_man: "🙋‍♂️",
-	raising_hand_woman: "🙋‍♀️",
-	deaf_person: "🧏",
-	deaf_man: "🧏‍♂️",
-	deaf_woman: "🧏‍♀️",
-	bow: "🙇",
-	bowing_man: "🙇‍♂️",
-	bowing_woman: "🙇‍♀️",
-	facepalm: "🤦",
-	man_facepalming: "🤦‍♂️",
-	woman_facepalming: "🤦‍♀️",
-	shrug: "🤷",
-	man_shrugging: "🤷‍♂️",
-	woman_shrugging: "🤷‍♀️",
-	health_worker: "🧑‍⚕️",
-	man_health_worker: "👨‍⚕️",
-	woman_health_worker: "👩‍⚕️",
-	student: "🧑‍🎓",
-	man_student: "👨‍🎓",
-	woman_student: "👩‍🎓",
-	teacher: "🧑‍🏫",
-	man_teacher: "👨‍🏫",
-	woman_teacher: "👩‍🏫",
-	judge: "🧑‍⚖️",
-	man_judge: "👨‍⚖️",
-	woman_judge: "👩‍⚖️",
-	farmer: "🧑‍🌾",
-	man_farmer: "👨‍🌾",
-	woman_farmer: "👩‍🌾",
-	cook: "🧑‍🍳",
-	man_cook: "👨‍🍳",
-	woman_cook: "👩‍🍳",
-	mechanic: "🧑‍🔧",
-	man_mechanic: "👨‍🔧",
-	woman_mechanic: "👩‍🔧",
-	factory_worker: "🧑‍🏭",
-	man_factory_worker: "👨‍🏭",
-	woman_factory_worker: "👩‍🏭",
-	office_worker: "🧑‍💼",
-	man_office_worker: "👨‍💼",
-	woman_office_worker: "👩‍💼",
-	scientist: "🧑‍🔬",
-	man_scientist: "👨‍🔬",
-	woman_scientist: "👩‍🔬",
-	technologist: "🧑‍💻",
-	man_technologist: "👨‍💻",
-	woman_technologist: "👩‍💻",
-	singer: "🧑‍🎤",
-	man_singer: "👨‍🎤",
-	woman_singer: "👩‍🎤",
-	artist: "🧑‍🎨",
-	man_artist: "👨‍🎨",
-	woman_artist: "👩‍🎨",
-	pilot: "🧑‍✈️",
-	man_pilot: "👨‍✈️",
-	woman_pilot: "👩‍✈️",
-	astronaut: "🧑‍🚀",
-	man_astronaut: "👨‍🚀",
-	woman_astronaut: "👩‍🚀",
-	firefighter: "🧑‍🚒",
-	man_firefighter: "👨‍🚒",
-	woman_firefighter: "👩‍🚒",
-	police_officer: "👮",
-	cop: "👮",
-	policeman: "👮‍♂️",
-	policewoman: "👮‍♀️",
-	detective: "🕵️",
-	male_detective: "🕵️‍♂️",
-	female_detective: "🕵️‍♀️",
-	guard: "💂",
-	guardsman: "💂‍♂️",
-	guardswoman: "💂‍♀️",
-	ninja: "🥷",
-	construction_worker: "👷",
-	construction_worker_man: "👷‍♂️",
-	construction_worker_woman: "👷‍♀️",
-	prince: "🤴",
-	princess: "👸",
-	person_with_turban: "👳",
-	man_with_turban: "👳‍♂️",
-	woman_with_turban: "👳‍♀️",
-	man_with_gua_pi_mao: "👲",
-	woman_with_headscarf: "🧕",
-	person_in_tuxedo: "🤵",
-	man_in_tuxedo: "🤵‍♂️",
-	woman_in_tuxedo: "🤵‍♀️",
-	person_with_veil: "👰",
-	man_with_veil: "👰‍♂️",
-	woman_with_veil: "👰‍♀️",
-	bride_with_veil: "👰‍♀️",
-	pregnant_woman: "🤰",
-	breast_feeding: "🤱",
-	woman_feeding_baby: "👩‍🍼",
-	man_feeding_baby: "👨‍🍼",
-	person_feeding_baby: "🧑‍🍼",
-	angel: "👼",
-	santa: "🎅",
-	mrs_claus: "🤶",
-	mx_claus: "🧑‍🎄",
-	superhero: "🦸",
-	superhero_man: "🦸‍♂️",
-	superhero_woman: "🦸‍♀️",
-	supervillain: "🦹",
-	supervillain_man: "🦹‍♂️",
-	supervillain_woman: "🦹‍♀️",
-	mage: "🧙",
-	mage_man: "🧙‍♂️",
-	mage_woman: "🧙‍♀️",
-	fairy: "🧚",
-	fairy_man: "🧚‍♂️",
-	fairy_woman: "🧚‍♀️",
-	vampire: "🧛",
-	vampire_man: "🧛‍♂️",
-	vampire_woman: "🧛‍♀️",
-	merperson: "🧜",
-	merman: "🧜‍♂️",
-	mermaid: "🧜‍♀️",
-	elf: "🧝",
-	elf_man: "🧝‍♂️",
-	elf_woman: "🧝‍♀️",
-	genie: "🧞",
-	genie_man: "🧞‍♂️",
-	genie_woman: "🧞‍♀️",
-	zombie: "🧟",
-	zombie_man: "🧟‍♂️",
-	zombie_woman: "🧟‍♀️",
-	massage: "💆",
-	massage_man: "💆‍♂️",
-	massage_woman: "💆‍♀️",
-	haircut: "💇",
-	haircut_man: "💇‍♂️",
-	haircut_woman: "💇‍♀️",
-	walking: "🚶",
-	walking_man: "🚶‍♂️",
-	walking_woman: "🚶‍♀️",
-	standing_person: "🧍",
-	standing_man: "🧍‍♂️",
-	standing_woman: "🧍‍♀️",
-	kneeling_person: "🧎",
-	kneeling_man: "🧎‍♂️",
-	kneeling_woman: "🧎‍♀️",
-	person_with_probing_cane: "🧑‍🦯",
-	man_with_probing_cane: "👨‍🦯",
-	woman_with_probing_cane: "👩‍🦯",
-	person_in_motorized_wheelchair: "🧑‍🦼",
-	man_in_motorized_wheelchair: "👨‍🦼",
-	woman_in_motorized_wheelchair: "👩‍🦼",
-	person_in_manual_wheelchair: "🧑‍🦽",
-	man_in_manual_wheelchair: "👨‍🦽",
-	woman_in_manual_wheelchair: "👩‍🦽",
-	runner: "🏃",
-	running: "🏃",
-	running_man: "🏃‍♂️",
-	running_woman: "🏃‍♀️",
-	woman_dancing: "💃",
-	dancer: "💃",
-	man_dancing: "🕺",
-	business_suit_levitating: "🕴️",
-	dancers: "👯",
-	dancing_men: "👯‍♂️",
-	dancing_women: "👯‍♀️",
-	sauna_person: "🧖",
-	sauna_man: "🧖‍♂️",
-	sauna_woman: "🧖‍♀️",
-	climbing: "🧗",
-	climbing_man: "🧗‍♂️",
-	climbing_woman: "🧗‍♀️",
-	person_fencing: "🤺",
-	horse_racing: "🏇",
-	skier: "⛷️",
-	snowboarder: "🏂",
-	golfing: "🏌️",
-	golfing_man: "🏌️‍♂️",
-	golfing_woman: "🏌️‍♀️",
-	surfer: "🏄",
-	surfing_man: "🏄‍♂️",
-	surfing_woman: "🏄‍♀️",
-	rowboat: "🚣",
-	rowing_man: "🚣‍♂️",
-	rowing_woman: "🚣‍♀️",
-	swimmer: "🏊",
-	swimming_man: "🏊‍♂️",
-	swimming_woman: "🏊‍♀️",
-	bouncing_ball_person: "⛹️",
-	bouncing_ball_man: "⛹️‍♂️",
-	basketball_man: "⛹️‍♂️",
-	bouncing_ball_woman: "⛹️‍♀️",
-	basketball_woman: "⛹️‍♀️",
-	weight_lifting: "🏋️",
-	weight_lifting_man: "🏋️‍♂️",
-	weight_lifting_woman: "🏋️‍♀️",
-	bicyclist: "🚴",
-	biking_man: "🚴‍♂️",
-	biking_woman: "🚴‍♀️",
-	mountain_bicyclist: "🚵",
-	mountain_biking_man: "🚵‍♂️",
-	mountain_biking_woman: "🚵‍♀️",
-	cartwheeling: "🤸",
-	man_cartwheeling: "🤸‍♂️",
-	woman_cartwheeling: "🤸‍♀️",
-	wrestling: "🤼",
-	men_wrestling: "🤼‍♂️",
-	women_wrestling: "🤼‍♀️",
-	water_polo: "🤽",
-	man_playing_water_polo: "🤽‍♂️",
-	woman_playing_water_polo: "🤽‍♀️",
-	handball_person: "🤾",
-	man_playing_handball: "🤾‍♂️",
-	woman_playing_handball: "🤾‍♀️",
-	juggling_person: "🤹",
-	man_juggling: "🤹‍♂️",
-	woman_juggling: "🤹‍♀️",
-	lotus_position: "🧘",
-	lotus_position_man: "🧘‍♂️",
-	lotus_position_woman: "🧘‍♀️",
-	bath: "🛀",
-	sleeping_bed: "🛌",
-	people_holding_hands: "🧑‍🤝‍🧑",
-	two_women_holding_hands: "👭",
-	couple: "👫",
-	two_men_holding_hands: "👬",
-	couplekiss: "💏",
-	couplekiss_man_woman: "👩‍❤️‍💋‍👨",
-	couplekiss_man_man: "👨‍❤️‍💋‍👨",
-	couplekiss_woman_woman: "👩‍❤️‍💋‍👩",
-	couple_with_heart: "💑",
-	couple_with_heart_woman_man: "👩‍❤️‍👨",
-	couple_with_heart_man_man: "👨‍❤️‍👨",
-	couple_with_heart_woman_woman: "👩‍❤️‍👩",
-	family: "👪",
-	family_man_woman_boy: "👨‍👩‍👦",
-	family_man_woman_girl: "👨‍👩‍👧",
-	family_man_woman_girl_boy: "👨‍👩‍👧‍👦",
-	family_man_woman_boy_boy: "👨‍👩‍👦‍👦",
-	family_man_woman_girl_girl: "👨‍👩‍👧‍👧",
-	family_man_man_boy: "👨‍👨‍👦",
-	family_man_man_girl: "👨‍👨‍👧",
-	family_man_man_girl_boy: "👨‍👨‍👧‍👦",
-	family_man_man_boy_boy: "👨‍👨‍👦‍👦",
-	family_man_man_girl_girl: "👨‍👨‍👧‍👧",
-	family_woman_woman_boy: "👩‍👩‍👦",
-	family_woman_woman_girl: "👩‍👩‍👧",
-	family_woman_woman_girl_boy: "👩‍👩‍👧‍👦",
-	family_woman_woman_boy_boy: "👩‍👩‍👦‍👦",
-	family_woman_woman_girl_girl: "👩‍👩‍👧‍👧",
-	family_man_boy: "👨‍👦",
-	family_man_boy_boy: "👨‍👦‍👦",
-	family_man_girl: "👨‍👧",
-	family_man_girl_boy: "👨‍👧‍👦",
-	family_man_girl_girl: "👨‍👧‍👧",
-	family_woman_boy: "👩‍👦",
-	family_woman_boy_boy: "👩‍👦‍👦",
-	family_woman_girl: "👩‍👧",
-	family_woman_girl_boy: "👩‍👧‍👦",
-	family_woman_girl_girl: "👩‍👧‍👧",
-	speaking_head: "🗣️",
-	bust_in_silhouette: "👤",
-	busts_in_silhouette: "👥",
-	people_hugging: "🫂",
-	footprints: "👣",
-	monkey_face: "🐵",
-	monkey: "🐒",
-	gorilla: "🦍",
-	orangutan: "🦧",
-	dog: "🐶",
-	dog2: "🐕",
-	guide_dog: "🦮",
-	service_dog: "🐕‍🦺",
-	poodle: "🐩",
-	wolf: "🐺",
-	fox_face: "🦊",
-	raccoon: "🦝",
-	cat: "🐱",
-	cat2: "🐈",
-	black_cat: "🐈‍⬛",
-	lion: "🦁",
-	tiger: "🐯",
-	tiger2: "🐅",
-	leopard: "🐆",
-	horse: "🐴",
-	racehorse: "🐎",
-	unicorn: "🦄",
-	zebra: "🦓",
-	deer: "🦌",
-	bison: "🦬",
-	cow: "🐮",
-	ox: "🐂",
-	water_buffalo: "🐃",
-	cow2: "🐄",
-	pig: "🐷",
-	pig2: "🐖",
-	boar: "🐗",
-	pig_nose: "🐽",
-	ram: "🐏",
-	sheep: "🐑",
-	goat: "🐐",
-	dromedary_camel: "🐪",
-	camel: "🐫",
-	llama: "🦙",
-	giraffe: "🦒",
-	elephant: "🐘",
-	mammoth: "🦣",
-	rhinoceros: "🦏",
-	hippopotamus: "🦛",
-	mouse: "🐭",
-	mouse2: "🐁",
-	rat: "🐀",
-	hamster: "🐹",
-	rabbit: "🐰",
-	rabbit2: "🐇",
-	chipmunk: "🐿️",
-	beaver: "🦫",
-	hedgehog: "🦔",
-	bat: "🦇",
-	bear: "🐻",
-	polar_bear: "🐻‍❄️",
-	koala: "🐨",
-	panda_face: "🐼",
-	sloth: "🦥",
-	otter: "🦦",
-	skunk: "🦨",
-	kangaroo: "🦘",
-	badger: "🦡",
-	feet: "🐾",
-	paw_prints: "🐾",
-	turkey: "🦃",
-	chicken: "🐔",
-	rooster: "🐓",
-	hatching_chick: "🐣",
-	baby_chick: "🐤",
-	hatched_chick: "🐥",
-	bird: "🐦",
-	penguin: "🐧",
-	dove: "🕊️",
-	eagle: "🦅",
-	duck: "🦆",
-	swan: "🦢",
-	owl: "🦉",
-	dodo: "🦤",
-	feather: "🪶",
-	flamingo: "🦩",
-	peacock: "🦚",
-	parrot: "🦜",
-	frog: "🐸",
-	crocodile: "🐊",
-	turtle: "🐢",
-	lizard: "🦎",
-	snake: "🐍",
-	dragon_face: "🐲",
-	dragon: "🐉",
-	sauropod: "🦕",
-	"t-rex": "🦖",
-	whale: "🐳",
-	whale2: "🐋",
-	dolphin: "🐬",
-	flipper: "🐬",
-	seal: "🦭",
-	fish: "🐟",
-	tropical_fish: "🐠",
-	blowfish: "🐡",
-	shark: "🦈",
-	octopus: "🐙",
-	shell: "🐚",
-	snail: "🐌",
-	butterfly: "🦋",
-	bug: "🐛",
-	ant: "🐜",
-	bee: "🐝",
-	honeybee: "🐝",
-	beetle: "🪲",
-	lady_beetle: "🐞",
-	cricket: "🦗",
-	cockroach: "🪳",
-	spider: "🕷️",
-	spider_web: "🕸️",
-	scorpion: "🦂",
-	mosquito: "🦟",
-	fly: "🪰",
-	worm: "🪱",
-	microbe: "🦠",
-	bouquet: "💐",
-	cherry_blossom: "🌸",
-	white_flower: "💮",
-	rosette: "🏵️",
-	rose: "🌹",
-	wilted_flower: "🥀",
-	hibiscus: "🌺",
-	sunflower: "🌻",
-	blossom: "🌼",
-	tulip: "🌷",
-	seedling: "🌱",
-	potted_plant: "🪴",
-	evergreen_tree: "🌲",
-	deciduous_tree: "🌳",
-	palm_tree: "🌴",
-	cactus: "🌵",
-	ear_of_rice: "🌾",
-	herb: "🌿",
-	shamrock: "☘️",
-	four_leaf_clover: "🍀",
-	maple_leaf: "🍁",
-	fallen_leaf: "🍂",
-	leaves: "🍃",
-	grapes: "🍇",
-	melon: "🍈",
-	watermelon: "🍉",
-	tangerine: "🍊",
-	orange: "🍊",
-	mandarin: "🍊",
-	lemon: "🍋",
-	banana: "🍌",
-	pineapple: "🍍",
-	mango: "🥭",
-	apple: "🍎",
-	green_apple: "🍏",
-	pear: "🍐",
-	peach: "🍑",
-	cherries: "🍒",
-	strawberry: "🍓",
-	blueberries: "🫐",
-	kiwi_fruit: "🥝",
-	tomato: "🍅",
-	olive: "🫒",
-	coconut: "🥥",
-	avocado: "🥑",
-	eggplant: "🍆",
-	potato: "🥔",
-	carrot: "🥕",
-	corn: "🌽",
-	hot_pepper: "🌶️",
-	bell_pepper: "🫑",
-	cucumber: "🥒",
-	leafy_green: "🥬",
-	broccoli: "🥦",
-	garlic: "🧄",
-	onion: "🧅",
-	mushroom: "🍄",
-	peanuts: "🥜",
-	chestnut: "🌰",
-	bread: "🍞",
-	croissant: "🥐",
-	baguette_bread: "🥖",
-	flatbread: "🫓",
-	pretzel: "🥨",
-	bagel: "🥯",
-	pancakes: "🥞",
-	waffle: "🧇",
-	cheese: "🧀",
-	meat_on_bone: "🍖",
-	poultry_leg: "🍗",
-	cut_of_meat: "🥩",
-	bacon: "🥓",
-	hamburger: "🍔",
-	fries: "🍟",
-	pizza: "🍕",
-	hotdog: "🌭",
-	sandwich: "🥪",
-	taco: "🌮",
-	burrito: "🌯",
-	tamale: "🫔",
-	stuffed_flatbread: "🥙",
-	falafel: "🧆",
-	egg: "🥚",
-	fried_egg: "🍳",
-	shallow_pan_of_food: "🥘",
-	stew: "🍲",
-	fondue: "🫕",
-	bowl_with_spoon: "🥣",
-	green_salad: "🥗",
-	popcorn: "🍿",
-	butter: "🧈",
-	salt: "🧂",
-	canned_food: "🥫",
-	bento: "🍱",
-	rice_cracker: "🍘",
-	rice_ball: "🍙",
-	rice: "🍚",
-	curry: "🍛",
-	ramen: "🍜",
-	spaghetti: "🍝",
-	sweet_potato: "🍠",
-	oden: "🍢",
-	sushi: "🍣",
-	fried_shrimp: "🍤",
-	fish_cake: "🍥",
-	moon_cake: "🥮",
-	dango: "🍡",
-	dumpling: "🥟",
-	fortune_cookie: "🥠",
-	takeout_box: "🥡",
-	crab: "🦀",
-	lobster: "🦞",
-	shrimp: "🦐",
-	squid: "🦑",
-	oyster: "🦪",
-	icecream: "🍦",
-	shaved_ice: "🍧",
-	ice_cream: "🍨",
-	doughnut: "🍩",
-	cookie: "🍪",
-	birthday: "🎂",
-	cake: "🍰",
-	cupcake: "🧁",
-	pie: "🥧",
-	chocolate_bar: "🍫",
-	candy: "🍬",
-	lollipop: "🍭",
-	custard: "🍮",
-	honey_pot: "🍯",
-	baby_bottle: "🍼",
-	milk_glass: "🥛",
-	coffee: "☕",
-	teapot: "🫖",
-	tea: "🍵",
-	sake: "🍶",
-	champagne: "🍾",
-	wine_glass: "🍷",
-	cocktail: "🍸",
-	tropical_drink: "🍹",
-	beer: "🍺",
-	beers: "🍻",
-	clinking_glasses: "🥂",
-	tumbler_glass: "🥃",
-	cup_with_straw: "🥤",
-	bubble_tea: "🧋",
-	beverage_box: "🧃",
-	mate: "🧉",
-	ice_cube: "🧊",
-	chopsticks: "🥢",
-	plate_with_cutlery: "🍽️",
-	fork_and_knife: "🍴",
-	spoon: "🥄",
-	hocho: "🔪",
-	knife: "🔪",
-	amphora: "🏺",
-	earth_africa: "🌍",
-	earth_americas: "🌎",
-	earth_asia: "🌏",
-	globe_with_meridians: "🌐",
-	world_map: "🗺️",
-	japan: "🗾",
-	compass: "🧭",
-	mountain_snow: "🏔️",
-	mountain: "⛰️",
-	volcano: "🌋",
-	mount_fuji: "🗻",
-	camping: "🏕️",
-	beach_umbrella: "🏖️",
-	desert: "🏜️",
-	desert_island: "🏝️",
-	national_park: "🏞️",
-	stadium: "🏟️",
-	classical_building: "🏛️",
-	building_construction: "🏗️",
-	bricks: "🧱",
-	rock: "🪨",
-	wood: "🪵",
-	hut: "🛖",
-	houses: "🏘️",
-	derelict_house: "🏚️",
-	house: "🏠",
-	house_with_garden: "🏡",
-	office: "🏢",
-	post_office: "🏣",
-	european_post_office: "🏤",
-	hospital: "🏥",
-	bank: "🏦",
-	hotel: "🏨",
-	love_hotel: "🏩",
-	convenience_store: "🏪",
-	school: "🏫",
-	department_store: "🏬",
-	factory: "🏭",
-	japanese_castle: "🏯",
-	european_castle: "🏰",
-	wedding: "💒",
-	tokyo_tower: "🗼",
-	statue_of_liberty: "🗽",
-	church: "⛪",
-	mosque: "🕌",
-	hindu_temple: "🛕",
-	synagogue: "🕍",
-	shinto_shrine: "⛩️",
-	kaaba: "🕋",
-	fountain: "⛲",
-	tent: "⛺",
-	foggy: "🌁",
-	night_with_stars: "🌃",
-	cityscape: "🏙️",
-	sunrise_over_mountains: "🌄",
-	sunrise: "🌅",
-	city_sunset: "🌆",
-	city_sunrise: "🌇",
-	bridge_at_night: "🌉",
-	hotsprings: "♨️",
-	carousel_horse: "🎠",
-	ferris_wheel: "🎡",
-	roller_coaster: "🎢",
-	barber: "💈",
-	circus_tent: "🎪",
-	steam_locomotive: "🚂",
-	railway_car: "🚃",
-	bullettrain_side: "🚄",
-	bullettrain_front: "🚅",
-	train2: "🚆",
-	metro: "🚇",
-	light_rail: "🚈",
-	station: "🚉",
-	tram: "🚊",
-	monorail: "🚝",
-	mountain_railway: "🚞",
-	train: "🚋",
-	bus: "🚌",
-	oncoming_bus: "🚍",
-	trolleybus: "🚎",
-	minibus: "🚐",
-	ambulance: "🚑",
-	fire_engine: "🚒",
-	police_car: "🚓",
-	oncoming_police_car: "🚔",
-	taxi: "🚕",
-	oncoming_taxi: "🚖",
-	car: "🚗",
-	red_car: "🚗",
-	oncoming_automobile: "🚘",
-	blue_car: "🚙",
-	pickup_truck: "🛻",
-	truck: "🚚",
-	articulated_lorry: "🚛",
-	tractor: "🚜",
-	racing_car: "🏎️",
-	motorcycle: "🏍️",
-	motor_scooter: "🛵",
-	manual_wheelchair: "🦽",
-	motorized_wheelchair: "🦼",
-	auto_rickshaw: "🛺",
-	bike: "🚲",
-	kick_scooter: "🛴",
-	skateboard: "🛹",
-	roller_skate: "🛼",
-	busstop: "🚏",
-	motorway: "🛣️",
-	railway_track: "🛤️",
-	oil_drum: "🛢️",
-	fuelpump: "⛽",
-	rotating_light: "🚨",
-	traffic_light: "🚥",
-	vertical_traffic_light: "🚦",
-	stop_sign: "🛑",
-	construction: "🚧",
-	anchor: "âš“",
-	boat: "⛵",
-	sailboat: "⛵",
-	canoe: "🛶",
-	speedboat: "🚤",
-	passenger_ship: "🛳️",
-	ferry: "⛴️",
-	motor_boat: "🛥️",
-	ship: "🚢",
-	airplane: "✈️",
-	small_airplane: "🛩️",
-	flight_departure: "🛫",
-	flight_arrival: "🛬",
-	parachute: "🪂",
-	seat: "💺",
-	helicopter: "🚁",
-	suspension_railway: "🚟",
-	mountain_cableway: "🚠",
-	aerial_tramway: "🚡",
-	artificial_satellite: "🛰️",
-	rocket: "🚀",
-	flying_saucer: "🛸",
-	bellhop_bell: "🛎️",
-	luggage: "🧳",
-	hourglass: "⌛",
-	hourglass_flowing_sand: "⏳",
-	watch: "⌚",
-	alarm_clock: "⏰",
-	stopwatch: "⏱️",
-	timer_clock: "⏲️",
-	mantelpiece_clock: "🕰️",
-	clock12: "🕛",
-	clock1230: "🕧",
-	clock1: "🕐",
-	clock130: "🕜",
-	clock2: "🕑",
-	clock230: "🕝",
-	clock3: "🕒",
-	clock330: "🕞",
-	clock4: "🕓",
-	clock430: "🕟",
-	clock5: "🕔",
-	clock530: "🕠",
-	clock6: "🕕",
-	clock630: "🕡",
-	clock7: "🕖",
-	clock730: "🕢",
-	clock8: "🕗",
-	clock830: "🕣",
-	clock9: "🕘",
-	clock930: "🕤",
-	clock10: "🕙",
-	clock1030: "🕥",
-	clock11: "🕚",
-	clock1130: "🕦",
-	new_moon: "🌑",
-	waxing_crescent_moon: "🌒",
-	first_quarter_moon: "🌓",
-	moon: "🌔",
-	waxing_gibbous_moon: "🌔",
-	full_moon: "🌕",
-	waning_gibbous_moon: "🌖",
-	last_quarter_moon: "🌗",
-	waning_crescent_moon: "🌘",
-	crescent_moon: "🌙",
-	new_moon_with_face: "🌚",
-	first_quarter_moon_with_face: "🌛",
-	last_quarter_moon_with_face: "🌜",
-	thermometer: "🌡️",
-	sunny: "☀️",
-	full_moon_with_face: "🌝",
-	sun_with_face: "🌞",
-	ringed_planet: "🪐",
-	star: "⭐",
-	star2: "🌟",
-	stars: "🌠",
-	milky_way: "🌌",
-	cloud: "☁️",
-	partly_sunny: "â›…",
-	cloud_with_lightning_and_rain: "⛈️",
-	sun_behind_small_cloud: "🌤️",
-	sun_behind_large_cloud: "🌥️",
-	sun_behind_rain_cloud: "🌦️",
-	cloud_with_rain: "🌧️",
-	cloud_with_snow: "🌨️",
-	cloud_with_lightning: "🌩️",
-	tornado: "🌪️",
-	fog: "🌫️",
-	wind_face: "🌬️",
-	cyclone: "🌀",
-	rainbow: "🌈",
-	closed_umbrella: "🌂",
-	open_umbrella: "☂️",
-	umbrella: "☔",
-	parasol_on_ground: "⛱️",
-	zap: "âš¡",
-	snowflake: "❄️",
-	snowman_with_snow: "☃️",
-	snowman: "⛄",
-	comet: "☄️",
-	fire: "🔥",
-	droplet: "💧",
-	ocean: "🌊",
-	jack_o_lantern: "🎃",
-	christmas_tree: "🎄",
-	fireworks: "🎆",
-	sparkler: "🎇",
-	firecracker: "🧨",
-	sparkles: "✨",
-	balloon: "🎈",
-	tada: "🎉",
-	confetti_ball: "🎊",
-	tanabata_tree: "🎋",
-	bamboo: "🎍",
-	dolls: "🎎",
-	flags: "🎏",
-	wind_chime: "🎐",
-	rice_scene: "🎑",
-	red_envelope: "🧧",
-	ribbon: "🎀",
-	gift: "🎁",
-	reminder_ribbon: "🎗️",
-	tickets: "🎟️",
-	ticket: "🎫",
-	medal_military: "🎖️",
-	trophy: "🏆",
-	medal_sports: "🏅",
-	"1st_place_medal": "🥇",
-	"2nd_place_medal": "🥈",
-	"3rd_place_medal": "🥉",
-	soccer: "âš½",
-	baseball: "âš¾",
-	softball: "🥎",
-	basketball: "🏀",
-	volleyball: "🏐",
-	football: "🏈",
-	rugby_football: "🏉",
-	tennis: "🎾",
-	flying_disc: "🥏",
-	bowling: "🎳",
-	cricket_game: "🏏",
-	field_hockey: "🏑",
-	ice_hockey: "🏒",
-	lacrosse: "🥍",
-	ping_pong: "🏓",
-	badminton: "🏸",
-	boxing_glove: "🥊",
-	martial_arts_uniform: "🥋",
-	goal_net: "🥅",
-	golf: "⛳",
-	ice_skate: "⛸️",
-	fishing_pole_and_fish: "🎣",
-	diving_mask: "🤿",
-	running_shirt_with_sash: "🎽",
-	ski: "🎿",
-	sled: "🛷",
-	curling_stone: "🥌",
-	dart: "🎯",
-	yo_yo: "🪀",
-	kite: "🪁",
-	"8ball": "🎱",
-	crystal_ball: "🔮",
-	magic_wand: "🪄",
-	nazar_amulet: "🧿",
-	video_game: "🎮",
-	joystick: "🕹️",
-	slot_machine: "🎰",
-	game_die: "🎲",
-	jigsaw: "🧩",
-	teddy_bear: "🧸",
-	pinata: "🪅",
-	nesting_dolls: "🪆",
-	spades: "♠️",
-	hearts: "♥️",
-	diamonds: "♦️",
-	clubs: "♣️",
-	chess_pawn: "♟️",
-	black_joker: "🃏",
-	mahjong: "🀄",
-	flower_playing_cards: "🎴",
-	performing_arts: "🎭",
-	framed_picture: "🖼️",
-	art: "🎨",
-	thread: "🧵",
-	sewing_needle: "🪡",
-	yarn: "🧶",
-	knot: "🪢",
-	eyeglasses: "👓",
-	dark_sunglasses: "🕶️",
-	goggles: "🥽",
-	lab_coat: "🥼",
-	safety_vest: "🦺",
-	necktie: "👔",
-	shirt: "👕",
-	tshirt: "👕",
-	jeans: "👖",
-	scarf: "🧣",
-	gloves: "🧤",
-	coat: "🧥",
-	socks: "🧦",
-	dress: "👗",
-	kimono: "👘",
-	sari: "🥻",
-	one_piece_swimsuit: "🩱",
-	swim_brief: "🩲",
-	shorts: "🩳",
-	bikini: "👙",
-	womans_clothes: "👚",
-	purse: "👛",
-	handbag: "👜",
-	pouch: "👝",
-	shopping: "🛍️",
-	school_satchel: "🎒",
-	thong_sandal: "🩴",
-	mans_shoe: "👞",
-	shoe: "👞",
-	athletic_shoe: "👟",
-	hiking_boot: "🥾",
-	flat_shoe: "🥿",
-	high_heel: "👠",
-	sandal: "👡",
-	ballet_shoes: "🩰",
-	boot: "👢",
-	crown: "👑",
-	womans_hat: "👒",
-	tophat: "🎩",
-	mortar_board: "🎓",
-	billed_cap: "🧢",
-	military_helmet: "🪖",
-	rescue_worker_helmet: "⛑️",
-	prayer_beads: "📿",
-	lipstick: "💄",
-	ring: "💍",
-	gem: "💎",
-	mute: "🔇",
-	speaker: "🔈",
-	sound: "🔉",
-	loud_sound: "🔊",
-	loudspeaker: "📢",
-	mega: "📣",
-	postal_horn: "📯",
-	bell: "🔔",
-	no_bell: "🔕",
-	musical_score: "🎼",
-	musical_note: "🎵",
-	notes: "🎶",
-	studio_microphone: "🎙️",
-	level_slider: "🎚️",
-	control_knobs: "🎛️",
-	microphone: "🎤",
-	headphones: "🎧",
-	radio: "📻",
-	saxophone: "🎷",
-	accordion: "🪗",
-	guitar: "🎸",
-	musical_keyboard: "🎹",
-	trumpet: "🎺",
-	violin: "🎻",
-	banjo: "🪕",
-	drum: "🥁",
-	long_drum: "🪘",
-	iphone: "📱",
-	calling: "📲",
-	phone: "☎️",
-	telephone: "☎️",
-	telephone_receiver: "📞",
-	pager: "📟",
-	fax: "📠",
-	battery: "🔋",
-	electric_plug: "🔌",
-	computer: "💻",
-	desktop_computer: "🖥️",
-	printer: "🖨️",
-	keyboard: "⌨️",
-	computer_mouse: "🖱️",
-	trackball: "🖲️",
-	minidisc: "💽",
-	floppy_disk: "💾",
-	cd: "💿",
-	dvd: "📀",
-	abacus: "🧮",
-	movie_camera: "🎥",
-	film_strip: "🎞️",
-	film_projector: "📽️",
-	clapper: "🎬",
-	tv: "📺",
-	camera: "📷",
-	camera_flash: "📸",
-	video_camera: "📹",
-	vhs: "📼",
-	mag: "🔍",
-	mag_right: "🔎",
-	candle: "🕯️",
-	bulb: "💡",
-	flashlight: "🔦",
-	izakaya_lantern: "🏮",
-	lantern: "🏮",
-	diya_lamp: "🪔",
-	notebook_with_decorative_cover: "📔",
-	closed_book: "📕",
-	book: "📖",
-	open_book: "📖",
-	green_book: "📗",
-	blue_book: "📘",
-	orange_book: "📙",
-	books: "📚",
-	notebook: "📓",
-	ledger: "📒",
-	page_with_curl: "📃",
-	scroll: "📜",
-	page_facing_up: "📄",
-	newspaper: "📰",
-	newspaper_roll: "🗞️",
-	bookmark_tabs: "📑",
-	bookmark: "🔖",
-	label: "🏷️",
-	moneybag: "💰",
-	coin: "🪙",
-	yen: "💴",
-	dollar: "💵",
-	euro: "💶",
-	pound: "💷",
-	money_with_wings: "💸",
-	credit_card: "💳",
-	receipt: "🧾",
-	chart: "💹",
-	envelope: "✉️",
-	email: "📧",
-	"e-mail": "📧",
-	incoming_envelope: "📨",
-	envelope_with_arrow: "📩",
-	outbox_tray: "📤",
-	inbox_tray: "📥",
-	package: "📦",
-	mailbox: "📫",
-	mailbox_closed: "📪",
-	mailbox_with_mail: "📬",
-	mailbox_with_no_mail: "📭",
-	postbox: "📮",
-	ballot_box: "🗳️",
-	pencil2: "✏️",
-	black_nib: "✒️",
-	fountain_pen: "🖋️",
-	pen: "🖊️",
-	paintbrush: "🖌️",
-	crayon: "🖍️",
-	memo: "📝",
-	pencil: "📝",
-	briefcase: "💼",
-	file_folder: "📁",
-	open_file_folder: "📂",
-	card_index_dividers: "🗂️",
-	date: "📅",
-	calendar: "📆",
-	spiral_notepad: "🗒️",
-	spiral_calendar: "🗓️",
-	card_index: "📇",
-	chart_with_upwards_trend: "📈",
-	chart_with_downwards_trend: "📉",
-	bar_chart: "📊",
-	clipboard: "📋",
-	pushpin: "📌",
-	round_pushpin: "📍",
-	paperclip: "📎",
-	paperclips: "🖇️",
-	straight_ruler: "📏",
-	triangular_ruler: "📐",
-	scissors: "✂️",
-	card_file_box: "🗃️",
-	file_cabinet: "🗄️",
-	wastebasket: "🗑️",
-	lock: "🔒",
-	unlock: "🔓",
-	lock_with_ink_pen: "🔏",
-	closed_lock_with_key: "🔐",
-	key: "🔑",
-	old_key: "🗝️",
-	hammer: "🔨",
-	axe: "🪓",
-	pick: "⛏️",
-	hammer_and_pick: "⚒️",
-	hammer_and_wrench: "🛠️",
-	dagger: "🗡️",
-	crossed_swords: "⚔️",
-	gun: "🔫",
-	boomerang: "🪃",
-	bow_and_arrow: "🏹",
-	shield: "🛡️",
-	carpentry_saw: "🪚",
-	wrench: "🔧",
-	screwdriver: "🪛",
-	nut_and_bolt: "🔩",
-	gear: "⚙️",
-	clamp: "🗜️",
-	balance_scale: "⚖️",
-	probing_cane: "🦯",
-	link: "🔗",
-	chains: "⛓️",
-	hook: "🪝",
-	toolbox: "🧰",
-	magnet: "🧲",
-	ladder: "🪜",
-	alembic: "⚗️",
-	test_tube: "🧪",
-	petri_dish: "🧫",
-	dna: "🧬",
-	microscope: "🔬",
-	telescope: "🔭",
-	satellite: "📡",
-	syringe: "💉",
-	drop_of_blood: "🩸",
-	pill: "💊",
-	adhesive_bandage: "🩹",
-	stethoscope: "🩺",
-	door: "🚪",
-	elevator: "🛗",
-	mirror: "🪞",
-	window: "🪟",
-	bed: "🛏️",
-	couch_and_lamp: "🛋️",
-	chair: "🪑",
-	toilet: "🚽",
-	plunger: "🪠",
-	shower: "🚿",
-	bathtub: "🛁",
-	mouse_trap: "🪤",
-	razor: "🪒",
-	lotion_bottle: "🧴",
-	safety_pin: "🧷",
-	broom: "🧹",
-	basket: "🧺",
-	roll_of_paper: "🧻",
-	bucket: "🪣",
-	soap: "🧼",
-	toothbrush: "🪥",
-	sponge: "🧽",
-	fire_extinguisher: "🧯",
-	shopping_cart: "🛒",
-	smoking: "🚬",
-	coffin: "⚰️",
-	headstone: "🪦",
-	funeral_urn: "⚱️",
-	moyai: "🗿",
-	placard: "🪧",
-	atm: "🏧",
-	put_litter_in_its_place: "🚮",
-	potable_water: "🚰",
-	wheelchair: "♿",
-	mens: "🚹",
-	womens: "🚺",
-	restroom: "🚻",
-	baby_symbol: "🚼",
-	wc: "🚾",
-	passport_control: "🛂",
-	customs: "🛃",
-	baggage_claim: "🛄",
-	left_luggage: "🛅",
-	warning: "⚠️",
-	children_crossing: "🚸",
-	no_entry: "â›”",
-	no_entry_sign: "🚫",
-	no_bicycles: "🚳",
-	no_smoking: "🚭",
-	do_not_litter: "🚯",
-	"non-potable_water": "🚱",
-	no_pedestrians: "🚷",
-	no_mobile_phones: "📵",
-	underage: "🔞",
-	radioactive: "☢️",
-	biohazard: "☣️",
-	arrow_up: "⬆️",
-	arrow_upper_right: "↗️",
-	arrow_right: "➡️",
-	arrow_lower_right: "↘️",
-	arrow_down: "⬇️",
-	arrow_lower_left: "↙️",
-	arrow_left: "⬅️",
-	arrow_upper_left: "↖️",
-	arrow_up_down: "↕️",
-	left_right_arrow: "↔️",
-	leftwards_arrow_with_hook: "↩️",
-	arrow_right_hook: "↪️",
-	arrow_heading_up: "⤴️",
-	arrow_heading_down: "⤵️",
-	arrows_clockwise: "🔃",
-	arrows_counterclockwise: "🔄",
-	back: "🔙",
-	end: "🔚",
-	on: "🔛",
-	soon: "🔜",
-	top: "🔝",
-	place_of_worship: "🛐",
-	atom_symbol: "⚛️",
-	om: "🕉️",
-	star_of_david: "✡️",
-	wheel_of_dharma: "☸️",
-	yin_yang: "☯️",
-	latin_cross: "✝️",
-	orthodox_cross: "☦️",
-	star_and_crescent: "☪️",
-	peace_symbol: "☮️",
-	menorah: "🕎",
-	six_pointed_star: "🔯",
-	aries: "♈",
-	taurus: "♉",
-	gemini: "♊",
-	cancer: "♋",
-	leo: "♌",
-	virgo: "♍",
-	libra: "♎",
-	scorpius: "♏",
-	sagittarius: "♐",
-	capricorn: "♑",
-	aquarius: "â™’",
-	pisces: "♓",
-	ophiuchus: "⛎",
-	twisted_rightwards_arrows: "🔀",
-	repeat: "🔁",
-	repeat_one: "🔂",
-	arrow_forward: "▶️",
-	fast_forward: "⏩",
-	next_track_button: "⏭️",
-	play_or_pause_button: "⏯️",
-	arrow_backward: "◀️",
-	rewind: "⏪",
-	previous_track_button: "⏮️",
-	arrow_up_small: "🔼",
-	arrow_double_up: "⏫",
-	arrow_down_small: "🔽",
-	arrow_double_down: "⏬",
-	pause_button: "⏸️",
-	stop_button: "⏹️",
-	record_button: "⏺️",
-	eject_button: "⏏️",
-	cinema: "🎦",
-	low_brightness: "🔅",
-	high_brightness: "🔆",
-	signal_strength: "📶",
-	vibration_mode: "📳",
-	mobile_phone_off: "📴",
-	female_sign: "♀️",
-	male_sign: "♂️",
-	transgender_symbol: "⚧️",
-	heavy_multiplication_x: "✖️",
-	heavy_plus_sign: "âž•",
-	heavy_minus_sign: "âž–",
-	heavy_division_sign: "âž—",
-	infinity: "♾️",
-	bangbang: "‼️",
-	interrobang: "⁉️",
-	question: "❓",
-	grey_question: "❔",
-	grey_exclamation: "❕",
-	exclamation: "❗",
-	heavy_exclamation_mark: "❗",
-	wavy_dash: "〰️",
-	currency_exchange: "💱",
-	heavy_dollar_sign: "💲",
-	medical_symbol: "⚕️",
-	recycle: "♻️",
-	fleur_de_lis: "⚜️",
-	trident: "🔱",
-	name_badge: "📛",
-	beginner: "🔰",
-	o: "â­•",
-	white_check_mark: "✅",
-	ballot_box_with_check: "☑️",
-	heavy_check_mark: "✔️",
-	x: "❌",
-	negative_squared_cross_mark: "❎",
-	curly_loop: "âž°",
-	loop: "âž¿",
-	part_alternation_mark: "〽️",
-	eight_spoked_asterisk: "✳️",
-	eight_pointed_black_star: "✴️",
-	sparkle: "❇️",
-	copyright: "©️",
-	registered: "®️",
-	tm: "™️",
-	hash: "#️⃣",
-	asterisk: "*️⃣",
-	zero: "0️⃣",
-	one: "1️⃣",
-	two: "2️⃣",
-	three: "3️⃣",
-	four: "4️⃣",
-	five: "5️⃣",
-	six: "6️⃣",
-	seven: "7️⃣",
-	eight: "8️⃣",
-	nine: "9️⃣",
-	keycap_ten: "🔟",
-	capital_abcd: "🔠",
-	abcd: "🔡",
-	symbols: "🔣",
-	abc: "🔤",
-	a: "🅰️",
-	ab: "🆎",
-	b: "🅱️",
-	cl: "🆑",
-	cool: "🆒",
-	free: "🆓",
-	information_source: "ℹ️",
-	id: "🆔",
-	m: "Ⓜ️",
-	new: "🆕",
-	ng: "🆖",
-	o2: "🅾️",
-	ok: "🆗",
-	parking: "🅿️",
-	sos: "🆘",
-	up: "🆙",
-	vs: "🆚",
-	koko: "🈁",
-	sa: "🈂️",
-	u6708: "🈷️",
-	u6709: "🈶",
-	u6307: "🈯",
-	ideograph_advantage: "🉐",
-	u5272: "🈹",
-	u7121: "🈚",
-	u7981: "🈲",
-	accept: "🉑",
-	u7533: "🈸",
-	u5408: "🈴",
-	u7a7a: "🈳",
-	congratulations: "㊗️",
-	secret: "㊙️",
-	u55b6: "🈺",
-	u6e80: "🈵",
-	red_circle: "🔴",
-	orange_circle: "🟠",
-	yellow_circle: "🟡",
-	green_circle: "🟢",
-	large_blue_circle: "🔵",
-	purple_circle: "🟣",
-	brown_circle: "🟤",
-	black_circle: "âš«",
-	white_circle: "⚪",
-	red_square: "🟥",
-	orange_square: "🟧",
-	yellow_square: "🟨",
-	green_square: "🟩",
-	blue_square: "🟦",
-	purple_square: "🟪",
-	brown_square: "🟫",
-	black_large_square: "⬛",
-	white_large_square: "⬜",
-	black_medium_square: "◼️",
-	white_medium_square: "◻️",
-	black_medium_small_square: "â—¾",
-	white_medium_small_square: "â—½",
-	black_small_square: "▪️",
-	white_small_square: "▫️",
-	large_orange_diamond: "🔶",
-	large_blue_diamond: "🔷",
-	small_orange_diamond: "🔸",
-	small_blue_diamond: "🔹",
-	small_red_triangle: "🔺",
-	small_red_triangle_down: "🔻",
-	diamond_shape_with_a_dot_inside: "💠",
-	radio_button: "🔘",
-	white_square_button: "🔳",
-	black_square_button: "🔲",
-	checkered_flag: "🏁",
-	triangular_flag_on_post: "🚩",
-	crossed_flags: "🎌",
-	black_flag: "🏴",
-	white_flag: "🏳️",
-	rainbow_flag: "🏳️‍🌈",
-	transgender_flag: "🏳️‍⚧️",
-	pirate_flag: "🏴‍☠️",
-	ascension_island: "🇦🇨",
-	andorra: "🇦🇩",
-	united_arab_emirates: "🇦🇪",
-	afghanistan: "🇦🇫",
-	antigua_barbuda: "🇦🇬",
-	anguilla: "🇦🇮",
-	albania: "🇦🇱",
-	armenia: "🇦🇲",
-	angola: "🇦🇴",
-	antarctica: "🇦🇶",
-	argentina: "🇦🇷",
-	american_samoa: "🇦🇸",
-	austria: "🇦🇹",
-	australia: "🇦🇺",
-	aruba: "🇦🇼",
-	aland_islands: "🇦🇽",
-	azerbaijan: "🇦🇿",
-	bosnia_herzegovina: "🇧🇦",
-	barbados: "🇧🇧",
-	bangladesh: "🇧🇩",
-	belgium: "🇧🇪",
-	burkina_faso: "🇧🇫",
-	bulgaria: "🇧🇬",
-	bahrain: "🇧🇭",
-	burundi: "🇧🇮",
-	benin: "🇧🇯",
-	st_barthelemy: "🇧🇱",
-	bermuda: "🇧🇲",
-	brunei: "🇧🇳",
-	bolivia: "🇧🇴",
-	caribbean_netherlands: "🇧🇶",
-	brazil: "🇧🇷",
-	bahamas: "🇧🇸",
-	bhutan: "🇧🇹",
-	bouvet_island: "🇧🇻",
-	botswana: "🇧🇼",
-	belarus: "🇧🇾",
-	belize: "🇧🇿",
-	canada: "🇨🇦",
-	cocos_islands: "🇨🇨",
-	congo_kinshasa: "🇨🇩",
-	central_african_republic: "🇨🇫",
-	congo_brazzaville: "🇨🇬",
-	switzerland: "🇨🇭",
-	cote_divoire: "🇨🇮",
-	cook_islands: "🇨🇰",
-	chile: "🇨🇱",
-	cameroon: "🇨🇲",
-	cn: "🇨🇳",
-	colombia: "🇨🇴",
-	clipperton_island: "🇨🇵",
-	costa_rica: "🇨🇷",
-	cuba: "🇨🇺",
-	cape_verde: "🇨🇻",
-	curacao: "🇨🇼",
-	christmas_island: "🇨🇽",
-	cyprus: "🇨🇾",
-	czech_republic: "🇨🇿",
-	de: "🇩🇪",
-	diego_garcia: "🇩🇬",
-	djibouti: "🇩🇯",
-	denmark: "🇩🇰",
-	dominica: "🇩🇲",
-	dominican_republic: "🇩🇴",
-	algeria: "🇩🇿",
-	ceuta_melilla: "🇪🇦",
-	ecuador: "🇪🇨",
-	estonia: "🇪🇪",
-	egypt: "🇪🇬",
-	western_sahara: "🇪🇭",
-	eritrea: "🇪🇷",
-	es: "🇪🇸",
-	ethiopia: "🇪🇹",
-	eu: "🇪🇺",
-	european_union: "🇪🇺",
-	finland: "🇫🇮",
-	fiji: "🇫🇯",
-	falkland_islands: "🇫🇰",
-	micronesia: "🇫🇲",
-	faroe_islands: "🇫🇴",
-	fr: "🇫🇷",
-	gabon: "🇬🇦",
-	gb: "🇬🇧",
-	uk: "🇬🇧",
-	grenada: "🇬🇩",
-	georgia: "🇬🇪",
-	french_guiana: "🇬🇫",
-	guernsey: "🇬🇬",
-	ghana: "🇬🇭",
-	gibraltar: "🇬🇮",
-	greenland: "🇬🇱",
-	gambia: "🇬🇲",
-	guinea: "🇬🇳",
-	guadeloupe: "🇬🇵",
-	equatorial_guinea: "🇬🇶",
-	greece: "🇬🇷",
-	south_georgia_south_sandwich_islands: "🇬🇸",
-	guatemala: "🇬🇹",
-	guam: "🇬🇺",
-	guinea_bissau: "🇬🇼",
-	guyana: "🇬🇾",
-	hong_kong: "🇭🇰",
-	heard_mcdonald_islands: "🇭🇲",
-	honduras: "🇭🇳",
-	croatia: "🇭🇷",
-	haiti: "🇭🇹",
-	hungary: "🇭🇺",
-	canary_islands: "🇮🇨",
-	indonesia: "🇮🇩",
-	ireland: "🇮🇪",
-	israel: "🇮🇱",
-	isle_of_man: "🇮🇲",
-	india: "🇮🇳",
-	british_indian_ocean_territory: "🇮🇴",
-	iraq: "🇮🇶",
-	iran: "🇮🇷",
-	iceland: "🇮🇸",
-	it: "🇮🇹",
-	jersey: "🇯🇪",
-	jamaica: "🇯🇲",
-	jordan: "🇯🇴",
-	jp: "🇯🇵",
-	kenya: "🇰🇪",
-	kyrgyzstan: "🇰🇬",
-	cambodia: "🇰🇭",
-	kiribati: "🇰🇮",
-	comoros: "🇰🇲",
-	st_kitts_nevis: "🇰🇳",
-	north_korea: "🇰🇵",
-	kr: "🇰🇷",
-	kuwait: "🇰🇼",
-	cayman_islands: "🇰🇾",
-	kazakhstan: "🇰🇿",
-	laos: "🇱🇦",
-	lebanon: "🇱🇧",
-	st_lucia: "🇱🇨",
-	liechtenstein: "🇱🇮",
-	sri_lanka: "🇱🇰",
-	liberia: "🇱🇷",
-	lesotho: "🇱🇸",
-	lithuania: "🇱🇹",
-	luxembourg: "🇱🇺",
-	latvia: "🇱🇻",
-	libya: "🇱🇾",
-	morocco: "🇲🇦",
-	monaco: "🇲🇨",
-	moldova: "🇲🇩",
-	montenegro: "🇲🇪",
-	st_martin: "🇲🇫",
-	madagascar: "🇲🇬",
-	marshall_islands: "🇲🇭",
-	macedonia: "🇲🇰",
-	mali: "🇲🇱",
-	myanmar: "🇲🇲",
-	mongolia: "🇲🇳",
-	macau: "🇲🇴",
-	northern_mariana_islands: "🇲🇵",
-	martinique: "🇲🇶",
-	mauritania: "🇲🇷",
-	montserrat: "🇲🇸",
-	malta: "🇲🇹",
-	mauritius: "🇲🇺",
-	maldives: "🇲🇻",
-	malawi: "🇲🇼",
-	mexico: "🇲🇽",
-	malaysia: "🇲🇾",
-	mozambique: "🇲🇿",
-	namibia: "🇳🇦",
-	new_caledonia: "🇳🇨",
-	niger: "🇳🇪",
-	norfolk_island: "🇳🇫",
-	nigeria: "🇳🇬",
-	nicaragua: "🇳🇮",
-	netherlands: "🇳🇱",
-	norway: "🇳🇴",
-	nepal: "🇳🇵",
-	nauru: "🇳🇷",
-	niue: "🇳🇺",
-	new_zealand: "🇳🇿",
-	oman: "🇴🇲",
-	panama: "🇵🇦",
-	peru: "🇵🇪",
-	french_polynesia: "🇵🇫",
-	papua_new_guinea: "🇵🇬",
-	philippines: "🇵🇭",
-	pakistan: "🇵🇰",
-	poland: "🇵🇱",
-	st_pierre_miquelon: "🇵🇲",
-	pitcairn_islands: "🇵🇳",
-	puerto_rico: "🇵🇷",
-	palestinian_territories: "🇵🇸",
-	portugal: "🇵🇹",
-	palau: "🇵🇼",
-	paraguay: "🇵🇾",
-	qatar: "🇶🇦",
-	reunion: "🇷🇪",
-	romania: "🇷🇴",
-	serbia: "🇷🇸",
-	ru: "🇷🇺",
-	rwanda: "🇷🇼",
-	saudi_arabia: "🇸🇦",
-	solomon_islands: "🇸🇧",
-	seychelles: "🇸🇨",
-	sudan: "🇸🇩",
-	sweden: "🇸🇪",
-	singapore: "🇸🇬",
-	st_helena: "🇸🇭",
-	slovenia: "🇸🇮",
-	svalbard_jan_mayen: "🇸🇯",
-	slovakia: "🇸🇰",
-	sierra_leone: "🇸🇱",
-	san_marino: "🇸🇲",
-	senegal: "🇸🇳",
-	somalia: "🇸🇴",
-	suriname: "🇸🇷",
-	south_sudan: "🇸🇸",
-	sao_tome_principe: "🇸🇹",
-	el_salvador: "🇸🇻",
-	sint_maarten: "🇸🇽",
-	syria: "🇸🇾",
-	swaziland: "🇸🇿",
-	tristan_da_cunha: "🇹🇦",
-	turks_caicos_islands: "🇹🇨",
-	chad: "🇹🇩",
-	french_southern_territories: "🇹🇫",
-	togo: "🇹🇬",
-	thailand: "🇹🇭",
-	tajikistan: "🇹🇯",
-	tokelau: "🇹🇰",
-	timor_leste: "🇹🇱",
-	turkmenistan: "🇹🇲",
-	tunisia: "🇹🇳",
-	tonga: "🇹🇴",
-	tr: "🇹🇷",
-	trinidad_tobago: "🇹🇹",
-	tuvalu: "🇹🇻",
-	taiwan: "🇹🇼",
-	tanzania: "🇹🇿",
-	ukraine: "🇺🇦",
-	uganda: "🇺🇬",
-	us_outlying_islands: "🇺🇲",
-	united_nations: "🇺🇳",
-	us: "🇺🇸",
-	uruguay: "🇺🇾",
-	uzbekistan: "🇺🇿",
-	vatican_city: "🇻🇦",
-	st_vincent_grenadines: "🇻🇨",
-	venezuela: "🇻🇪",
-	british_virgin_islands: "🇻🇬",
-	us_virgin_islands: "🇻🇮",
-	vietnam: "🇻🇳",
-	vanuatu: "🇻🇺",
-	wallis_futuna: "🇼🇫",
-	samoa: "🇼🇸",
-	kosovo: "🇽🇰",
-	yemen: "🇾🇪",
-	mayotte: "🇾🇹",
-	south_africa: "🇿🇦",
-	zambia: "🇿🇲",
-	zimbabwe: "🇿🇼",
-	england: "🏴󠁧󠁢󠁥󠁮󠁧󠁿",
-	scotland: "🏴󠁧󠁢󠁳󠁣󠁴󠁿",
-	wales: "🏴󠁧󠁢󠁷󠁬󠁳󠁿",
+    "100": "💯",
+    "1234": "🔢",
+    grinning: "😀",
+    smiley: "😃",
+    smile: "😄",
+    grin: "😁",
+    laughing: "😆",
+    satisfied: "😆",
+    sweat_smile: "😅",
+    rofl: "🤣",
+    joy: "😂",
+    slightly_smiling_face: "🙂",
+    upside_down_face: "🙃",
+    wink: "😉",
+    blush: "😊",
+    innocent: "😇",
+    smiling_face_with_three_hearts: "🥰",
+    heart_eyes: "😍",
+    star_struck: "🤩",
+    kissing_heart: "😘",
+    kissing: "😗",
+    relaxed: "☺️",
+    kissing_closed_eyes: "😚",
+    kissing_smiling_eyes: "😙",
+    smiling_face_with_tear: "🥲",
+    yum: "😋",
+    stuck_out_tongue: "😛",
+    stuck_out_tongue_winking_eye: "😜",
+    zany_face: "🤪",
+    stuck_out_tongue_closed_eyes: "😝",
+    money_mouth_face: "🤑",
+    hugs: "🤗",
+    hand_over_mouth: "🤭",
+    shushing_face: "🤫",
+    thinking: "🤔",
+    zipper_mouth_face: "🤐",
+    raised_eyebrow: "🤨",
+    neutral_face: "😐",
+    expressionless: "😑",
+    no_mouth: "😶",
+    smirk: "😏",
+    unamused: "😒",
+    roll_eyes: "🙄",
+    grimacing: "😬",
+    lying_face: "🤥",
+    relieved: "😌",
+    pensive: "😔",
+    sleepy: "😪",
+    drooling_face: "🤤",
+    sleeping: "😴",
+    mask: "😷",
+    face_with_thermometer: "🤒",
+    face_with_head_bandage: "🤕",
+    nauseated_face: "🤢",
+    vomiting_face: "🤮",
+    sneezing_face: "🤧",
+    hot_face: "🥵",
+    cold_face: "🥶",
+    woozy_face: "🥴",
+    dizzy_face: "😵",
+    exploding_head: "🤯",
+    cowboy_hat_face: "🤠",
+    partying_face: "🥳",
+    disguised_face: "🥸",
+    sunglasses: "😎",
+    nerd_face: "🤓",
+    monocle_face: "🧐",
+    confused: "😕",
+    worried: "😟",
+    slightly_frowning_face: "🙁",
+    frowning_face: "☹️",
+    open_mouth: "😮",
+    hushed: "😯",
+    astonished: "😲",
+    flushed: "😳",
+    pleading_face: "🥺",
+    frowning: "😦",
+    anguished: "😧",
+    fearful: "😨",
+    cold_sweat: "😰",
+    disappointed_relieved: "😥",
+    cry: "😢",
+    sob: "😭",
+    scream: "😱",
+    confounded: "😖",
+    persevere: "😣",
+    disappointed: "😞",
+    sweat: "😓",
+    weary: "😩",
+    tired_face: "😫",
+    yawning_face: "🥱",
+    triumph: "😤",
+    rage: "😡",
+    pout: "😡",
+    angry: "😠",
+    cursing_face: "🤬",
+    smiling_imp: "😈",
+    imp: "👿",
+    skull: "💀",
+    skull_and_crossbones: "☠️",
+    hankey: "💩",
+    poop: "💩",
+    shit: "💩",
+    clown_face: "🤡",
+    japanese_ogre: "👹",
+    japanese_goblin: "👺",
+    ghost: "👻",
+    alien: "👽",
+    space_invader: "👾",
+    robot: "🤖",
+    smiley_cat: "😺",
+    smile_cat: "😸",
+    joy_cat: "😹",
+    heart_eyes_cat: "😻",
+    smirk_cat: "😼",
+    kissing_cat: "😽",
+    scream_cat: "🙀",
+    crying_cat_face: "😿",
+    pouting_cat: "😾",
+    see_no_evil: "🙈",
+    hear_no_evil: "🙉",
+    speak_no_evil: "🙊",
+    kiss: "💋",
+    love_letter: "💌",
+    cupid: "💘",
+    gift_heart: "💝",
+    sparkling_heart: "💖",
+    heartpulse: "💗",
+    heartbeat: "💓",
+    revolving_hearts: "💞",
+    two_hearts: "💕",
+    heart_decoration: "💟",
+    heavy_heart_exclamation: "❣️",
+    broken_heart: "💔",
+    heart: "❤️",
+    orange_heart: "🧡",
+    yellow_heart: "💛",
+    green_heart: "💚",
+    blue_heart: "💙",
+    purple_heart: "💜",
+    brown_heart: "🤎",
+    black_heart: "🖤",
+    white_heart: "🤍",
+    anger: "💢",
+    boom: "💥",
+    collision: "💥",
+    dizzy: "💫",
+    sweat_drops: "💦",
+    dash: "💨",
+    hole: "🕳️",
+    bomb: "💣",
+    speech_balloon: "💬",
+    eye_speech_bubble: "👁️‍🗨️",
+    left_speech_bubble: "🗨️",
+    right_anger_bubble: "🗯️",
+    thought_balloon: "💭",
+    zzz: "💤",
+    wave: "👋",
+    raised_back_of_hand: "🤚",
+    raised_hand_with_fingers_splayed: "🖐️",
+    hand: "✋",
+    raised_hand: "✋",
+    vulcan_salute: "🖖",
+    ok_hand: "👌",
+    pinched_fingers: "🤌",
+    pinching_hand: "🤏",
+    v: "✌️",
+    crossed_fingers: "🤞",
+    love_you_gesture: "🤟",
+    metal: "🤘",
+    call_me_hand: "🤙",
+    point_left: "👈",
+    point_right: "👉",
+    point_up_2: "👆",
+    middle_finger: "🖕",
+    fu: "🖕",
+    point_down: "👇",
+    point_up: "☝️",
+    "+1": "👍",
+    thumbsup: "👍",
+    "-1": "👎",
+    thumbsdown: "👎",
+    fist_raised: "✊",
+    fist: "✊",
+    fist_oncoming: "👊",
+    facepunch: "👊",
+    punch: "👊",
+    fist_left: "🤛",
+    fist_right: "🤜",
+    clap: "👏",
+    raised_hands: "🙌",
+    open_hands: "👐",
+    palms_up_together: "🤲",
+    handshake: "🤝",
+    pray: "🙏",
+    writing_hand: "✍️",
+    nail_care: "💅",
+    selfie: "🤳",
+    muscle: "💪",
+    mechanical_arm: "🦾",
+    mechanical_leg: "🦿",
+    leg: "🦵",
+    foot: "🦶",
+    ear: "👂",
+    ear_with_hearing_aid: "🦻",
+    nose: "👃",
+    brain: "🧠",
+    anatomical_heart: "🫀",
+    lungs: "🫁",
+    tooth: "🦷",
+    bone: "🦴",
+    eyes: "👀",
+    eye: "👁️",
+    tongue: "👅",
+    lips: "👄",
+    baby: "👶",
+    child: "🧒",
+    boy: "👦",
+    girl: "👧",
+    adult: "🧑",
+    blond_haired_person: "👱",
+    man: "👨",
+    bearded_person: "🧔",
+    red_haired_man: "👨‍🦰",
+    curly_haired_man: "👨‍🦱",
+    white_haired_man: "👨‍🦳",
+    bald_man: "👨‍🦲",
+    woman: "👩",
+    red_haired_woman: "👩‍🦰",
+    person_red_hair: "🧑‍🦰",
+    curly_haired_woman: "👩‍🦱",
+    person_curly_hair: "🧑‍🦱",
+    white_haired_woman: "👩‍🦳",
+    person_white_hair: "🧑‍🦳",
+    bald_woman: "👩‍🦲",
+    person_bald: "🧑‍🦲",
+    blond_haired_woman: "👱‍♀️",
+    blonde_woman: "👱‍♀️",
+    blond_haired_man: "👱‍♂️",
+    older_adult: "🧓",
+    older_man: "👴",
+    older_woman: "👵",
+    frowning_person: "🙍",
+    frowning_man: "🙍‍♂️",
+    frowning_woman: "🙍‍♀️",
+    pouting_face: "🙎",
+    pouting_man: "🙎‍♂️",
+    pouting_woman: "🙎‍♀️",
+    no_good: "🙅",
+    no_good_man: "🙅‍♂️",
+    ng_man: "🙅‍♂️",
+    no_good_woman: "🙅‍♀️",
+    ng_woman: "🙅‍♀️",
+    ok_person: "🙆",
+    ok_man: "🙆‍♂️",
+    ok_woman: "🙆‍♀️",
+    tipping_hand_person: "💁",
+    information_desk_person: "💁",
+    tipping_hand_man: "💁‍♂️",
+    sassy_man: "💁‍♂️",
+    tipping_hand_woman: "💁‍♀️",
+    sassy_woman: "💁‍♀️",
+    raising_hand: "🙋",
+    raising_hand_man: "🙋‍♂️",
+    raising_hand_woman: "🙋‍♀️",
+    deaf_person: "🧏",
+    deaf_man: "🧏‍♂️",
+    deaf_woman: "🧏‍♀️",
+    bow: "🙇",
+    bowing_man: "🙇‍♂️",
+    bowing_woman: "🙇‍♀️",
+    facepalm: "🤦",
+    man_facepalming: "🤦‍♂️",
+    woman_facepalming: "🤦‍♀️",
+    shrug: "🤷",
+    man_shrugging: "🤷‍♂️",
+    woman_shrugging: "🤷‍♀️",
+    health_worker: "🧑‍⚕️",
+    man_health_worker: "👨‍⚕️",
+    woman_health_worker: "👩‍⚕️",
+    student: "🧑‍🎓",
+    man_student: "👨‍🎓",
+    woman_student: "👩‍🎓",
+    teacher: "🧑‍🏫",
+    man_teacher: "👨‍🏫",
+    woman_teacher: "👩‍🏫",
+    judge: "🧑‍⚖️",
+    man_judge: "👨‍⚖️",
+    woman_judge: "👩‍⚖️",
+    farmer: "🧑‍🌾",
+    man_farmer: "👨‍🌾",
+    woman_farmer: "👩‍🌾",
+    cook: "🧑‍🍳",
+    man_cook: "👨‍🍳",
+    woman_cook: "👩‍🍳",
+    mechanic: "🧑‍🔧",
+    man_mechanic: "👨‍🔧",
+    woman_mechanic: "👩‍🔧",
+    factory_worker: "🧑‍🏭",
+    man_factory_worker: "👨‍🏭",
+    woman_factory_worker: "👩‍🏭",
+    office_worker: "🧑‍💼",
+    man_office_worker: "👨‍💼",
+    woman_office_worker: "👩‍💼",
+    scientist: "🧑‍🔬",
+    man_scientist: "👨‍🔬",
+    woman_scientist: "👩‍🔬",
+    technologist: "🧑‍💻",
+    man_technologist: "👨‍💻",
+    woman_technologist: "👩‍💻",
+    singer: "🧑‍🎤",
+    man_singer: "👨‍🎤",
+    woman_singer: "👩‍🎤",
+    artist: "🧑‍🎨",
+    man_artist: "👨‍🎨",
+    woman_artist: "👩‍🎨",
+    pilot: "🧑‍✈️",
+    man_pilot: "👨‍✈️",
+    woman_pilot: "👩‍✈️",
+    astronaut: "🧑‍🚀",
+    man_astronaut: "👨‍🚀",
+    woman_astronaut: "👩‍🚀",
+    firefighter: "🧑‍🚒",
+    man_firefighter: "👨‍🚒",
+    woman_firefighter: "👩‍🚒",
+    police_officer: "👮",
+    cop: "👮",
+    policeman: "👮‍♂️",
+    policewoman: "👮‍♀️",
+    detective: "🕵️",
+    male_detective: "🕵️‍♂️",
+    female_detective: "🕵️‍♀️",
+    guard: "💂",
+    guardsman: "💂‍♂️",
+    guardswoman: "💂‍♀️",
+    ninja: "🥷",
+    construction_worker: "👷",
+    construction_worker_man: "👷‍♂️",
+    construction_worker_woman: "👷‍♀️",
+    prince: "🤴",
+    princess: "👸",
+    person_with_turban: "👳",
+    man_with_turban: "👳‍♂️",
+    woman_with_turban: "👳‍♀️",
+    man_with_gua_pi_mao: "👲",
+    woman_with_headscarf: "🧕",
+    person_in_tuxedo: "🤵",
+    man_in_tuxedo: "🤵‍♂️",
+    woman_in_tuxedo: "🤵‍♀️",
+    person_with_veil: "👰",
+    man_with_veil: "👰‍♂️",
+    woman_with_veil: "👰‍♀️",
+    bride_with_veil: "👰‍♀️",
+    pregnant_woman: "🤰",
+    breast_feeding: "🤱",
+    woman_feeding_baby: "👩‍🍼",
+    man_feeding_baby: "👨‍🍼",
+    person_feeding_baby: "🧑‍🍼",
+    angel: "👼",
+    santa: "🎅",
+    mrs_claus: "🤶",
+    mx_claus: "🧑‍🎄",
+    superhero: "🦸",
+    superhero_man: "🦸‍♂️",
+    superhero_woman: "🦸‍♀️",
+    supervillain: "🦹",
+    supervillain_man: "🦹‍♂️",
+    supervillain_woman: "🦹‍♀️",
+    mage: "🧙",
+    mage_man: "🧙‍♂️",
+    mage_woman: "🧙‍♀️",
+    fairy: "🧚",
+    fairy_man: "🧚‍♂️",
+    fairy_woman: "🧚‍♀️",
+    vampire: "🧛",
+    vampire_man: "🧛‍♂️",
+    vampire_woman: "🧛‍♀️",
+    merperson: "🧜",
+    merman: "🧜‍♂️",
+    mermaid: "🧜‍♀️",
+    elf: "🧝",
+    elf_man: "🧝‍♂️",
+    elf_woman: "🧝‍♀️",
+    genie: "🧞",
+    genie_man: "🧞‍♂️",
+    genie_woman: "🧞‍♀️",
+    zombie: "🧟",
+    zombie_man: "🧟‍♂️",
+    zombie_woman: "🧟‍♀️",
+    massage: "💆",
+    massage_man: "💆‍♂️",
+    massage_woman: "💆‍♀️",
+    haircut: "💇",
+    haircut_man: "💇‍♂️",
+    haircut_woman: "💇‍♀️",
+    walking: "🚶",
+    walking_man: "🚶‍♂️",
+    walking_woman: "🚶‍♀️",
+    standing_person: "🧍",
+    standing_man: "🧍‍♂️",
+    standing_woman: "🧍‍♀️",
+    kneeling_person: "🧎",
+    kneeling_man: "🧎‍♂️",
+    kneeling_woman: "🧎‍♀️",
+    person_with_probing_cane: "🧑‍🦯",
+    man_with_probing_cane: "👨‍🦯",
+    woman_with_probing_cane: "👩‍🦯",
+    person_in_motorized_wheelchair: "🧑‍🦼",
+    man_in_motorized_wheelchair: "👨‍🦼",
+    woman_in_motorized_wheelchair: "👩‍🦼",
+    person_in_manual_wheelchair: "🧑‍🦽",
+    man_in_manual_wheelchair: "👨‍🦽",
+    woman_in_manual_wheelchair: "👩‍🦽",
+    runner: "🏃",
+    running: "🏃",
+    running_man: "🏃‍♂️",
+    running_woman: "🏃‍♀️",
+    woman_dancing: "💃",
+    dancer: "💃",
+    man_dancing: "🕺",
+    business_suit_levitating: "🕴️",
+    dancers: "👯",
+    dancing_men: "👯‍♂️",
+    dancing_women: "👯‍♀️",
+    sauna_person: "🧖",
+    sauna_man: "🧖‍♂️",
+    sauna_woman: "🧖‍♀️",
+    climbing: "🧗",
+    climbing_man: "🧗‍♂️",
+    climbing_woman: "🧗‍♀️",
+    person_fencing: "🤺",
+    horse_racing: "🏇",
+    skier: "⛷️",
+    snowboarder: "🏂",
+    golfing: "🏌️",
+    golfing_man: "🏌️‍♂️",
+    golfing_woman: "🏌️‍♀️",
+    surfer: "🏄",
+    surfing_man: "🏄‍♂️",
+    surfing_woman: "🏄‍♀️",
+    rowboat: "🚣",
+    rowing_man: "🚣‍♂️",
+    rowing_woman: "🚣‍♀️",
+    swimmer: "🏊",
+    swimming_man: "🏊‍♂️",
+    swimming_woman: "🏊‍♀️",
+    bouncing_ball_person: "⛹️",
+    bouncing_ball_man: "⛹️‍♂️",
+    basketball_man: "⛹️‍♂️",
+    bouncing_ball_woman: "⛹️‍♀️",
+    basketball_woman: "⛹️‍♀️",
+    weight_lifting: "🏋️",
+    weight_lifting_man: "🏋️‍♂️",
+    weight_lifting_woman: "🏋️‍♀️",
+    bicyclist: "🚴",
+    biking_man: "🚴‍♂️",
+    biking_woman: "🚴‍♀️",
+    mountain_bicyclist: "🚵",
+    mountain_biking_man: "🚵‍♂️",
+    mountain_biking_woman: "🚵‍♀️",
+    cartwheeling: "🤸",
+    man_cartwheeling: "🤸‍♂️",
+    woman_cartwheeling: "🤸‍♀️",
+    wrestling: "🤼",
+    men_wrestling: "🤼‍♂️",
+    women_wrestling: "🤼‍♀️",
+    water_polo: "🤽",
+    man_playing_water_polo: "🤽‍♂️",
+    woman_playing_water_polo: "🤽‍♀️",
+    handball_person: "🤾",
+    man_playing_handball: "🤾‍♂️",
+    woman_playing_handball: "🤾‍♀️",
+    juggling_person: "🤹",
+    man_juggling: "🤹‍♂️",
+    woman_juggling: "🤹‍♀️",
+    lotus_position: "🧘",
+    lotus_position_man: "🧘‍♂️",
+    lotus_position_woman: "🧘‍♀️",
+    bath: "🛀",
+    sleeping_bed: "🛌",
+    people_holding_hands: "🧑‍🤝‍🧑",
+    two_women_holding_hands: "👭",
+    couple: "👫",
+    two_men_holding_hands: "👬",
+    couplekiss: "💏",
+    couplekiss_man_woman: "👩‍❤️‍💋‍👨",
+    couplekiss_man_man: "👨‍❤️‍💋‍👨",
+    couplekiss_woman_woman: "👩‍❤️‍💋‍👩",
+    couple_with_heart: "💑",
+    couple_with_heart_woman_man: "👩‍❤️‍👨",
+    couple_with_heart_man_man: "👨‍❤️‍👨",
+    couple_with_heart_woman_woman: "👩‍❤️‍👩",
+    family: "👪",
+    family_man_woman_boy: "👨‍👩‍👦",
+    family_man_woman_girl: "👨‍👩‍👧",
+    family_man_woman_girl_boy: "👨‍👩‍👧‍👦",
+    family_man_woman_boy_boy: "👨‍👩‍👦‍👦",
+    family_man_woman_girl_girl: "👨‍👩‍👧‍👧",
+    family_man_man_boy: "👨‍👨‍👦",
+    family_man_man_girl: "👨‍👨‍👧",
+    family_man_man_girl_boy: "👨‍👨‍👧‍👦",
+    family_man_man_boy_boy: "👨‍👨‍👦‍👦",
+    family_man_man_girl_girl: "👨‍👨‍👧‍👧",
+    family_woman_woman_boy: "👩‍👩‍👦",
+    family_woman_woman_girl: "👩‍👩‍👧",
+    family_woman_woman_girl_boy: "👩‍👩‍👧‍👦",
+    family_woman_woman_boy_boy: "👩‍👩‍👦‍👦",
+    family_woman_woman_girl_girl: "👩‍👩‍👧‍👧",
+    family_man_boy: "👨‍👦",
+    family_man_boy_boy: "👨‍👦‍👦",
+    family_man_girl: "👨‍👧",
+    family_man_girl_boy: "👨‍👧‍👦",
+    family_man_girl_girl: "👨‍👧‍👧",
+    family_woman_boy: "👩‍👦",
+    family_woman_boy_boy: "👩‍👦‍👦",
+    family_woman_girl: "👩‍👧",
+    family_woman_girl_boy: "👩‍👧‍👦",
+    family_woman_girl_girl: "👩‍👧‍👧",
+    speaking_head: "🗣️",
+    bust_in_silhouette: "👤",
+    busts_in_silhouette: "👥",
+    people_hugging: "🫂",
+    footprints: "👣",
+    monkey_face: "🐵",
+    monkey: "🐒",
+    gorilla: "🦍",
+    orangutan: "🦧",
+    dog: "🐶",
+    dog2: "🐕",
+    guide_dog: "🦮",
+    service_dog: "🐕‍🦺",
+    poodle: "🐩",
+    wolf: "🐺",
+    fox_face: "🦊",
+    raccoon: "🦝",
+    cat: "🐱",
+    cat2: "🐈",
+    black_cat: "🐈‍⬛",
+    lion: "🦁",
+    tiger: "🐯",
+    tiger2: "🐅",
+    leopard: "🐆",
+    horse: "🐴",
+    racehorse: "🐎",
+    unicorn: "🦄",
+    zebra: "🦓",
+    deer: "🦌",
+    bison: "🦬",
+    cow: "🐮",
+    ox: "🐂",
+    water_buffalo: "🐃",
+    cow2: "🐄",
+    pig: "🐷",
+    pig2: "🐖",
+    boar: "🐗",
+    pig_nose: "🐽",
+    ram: "🐏",
+    sheep: "🐑",
+    goat: "🐐",
+    dromedary_camel: "🐪",
+    camel: "🐫",
+    llama: "🦙",
+    giraffe: "🦒",
+    elephant: "🐘",
+    mammoth: "🦣",
+    rhinoceros: "🦏",
+    hippopotamus: "🦛",
+    mouse: "🐭",
+    mouse2: "🐁",
+    rat: "🐀",
+    hamster: "🐹",
+    rabbit: "🐰",
+    rabbit2: "🐇",
+    chipmunk: "🐿️",
+    beaver: "🦫",
+    hedgehog: "🦔",
+    bat: "🦇",
+    bear: "🐻",
+    polar_bear: "🐻‍❄️",
+    koala: "🐨",
+    panda_face: "🐼",
+    sloth: "🦥",
+    otter: "🦦",
+    skunk: "🦨",
+    kangaroo: "🦘",
+    badger: "🦡",
+    feet: "🐾",
+    paw_prints: "🐾",
+    turkey: "🦃",
+    chicken: "🐔",
+    rooster: "🐓",
+    hatching_chick: "🐣",
+    baby_chick: "🐤",
+    hatched_chick: "🐥",
+    bird: "🐦",
+    penguin: "🐧",
+    dove: "🕊️",
+    eagle: "🦅",
+    duck: "🦆",
+    swan: "🦢",
+    owl: "🦉",
+    dodo: "🦤",
+    feather: "🪶",
+    flamingo: "🦩",
+    peacock: "🦚",
+    parrot: "🦜",
+    frog: "🐸",
+    crocodile: "🐊",
+    turtle: "🐢",
+    lizard: "🦎",
+    snake: "🐍",
+    dragon_face: "🐲",
+    dragon: "🐉",
+    sauropod: "🦕",
+    "t-rex": "🦖",
+    whale: "🐳",
+    whale2: "🐋",
+    dolphin: "🐬",
+    flipper: "🐬",
+    seal: "🦭",
+    fish: "🐟",
+    tropical_fish: "🐠",
+    blowfish: "🐡",
+    shark: "🦈",
+    octopus: "🐙",
+    shell: "🐚",
+    snail: "🐌",
+    butterfly: "🦋",
+    bug: "🐛",
+    ant: "🐜",
+    bee: "🐝",
+    honeybee: "🐝",
+    beetle: "🪲",
+    lady_beetle: "🐞",
+    cricket: "🦗",
+    cockroach: "🪳",
+    spider: "🕷️",
+    spider_web: "🕸️",
+    scorpion: "🦂",
+    mosquito: "🦟",
+    fly: "🪰",
+    worm: "🪱",
+    microbe: "🦠",
+    bouquet: "💐",
+    cherry_blossom: "🌸",
+    white_flower: "💮",
+    rosette: "🏵️",
+    rose: "🌹",
+    wilted_flower: "🥀",
+    hibiscus: "🌺",
+    sunflower: "🌻",
+    blossom: "🌼",
+    tulip: "🌷",
+    seedling: "🌱",
+    potted_plant: "🪴",
+    evergreen_tree: "🌲",
+    deciduous_tree: "🌳",
+    palm_tree: "🌴",
+    cactus: "🌵",
+    ear_of_rice: "🌾",
+    herb: "🌿",
+    shamrock: "☘️",
+    four_leaf_clover: "🍀",
+    maple_leaf: "🍁",
+    fallen_leaf: "🍂",
+    leaves: "🍃",
+    grapes: "🍇",
+    melon: "🍈",
+    watermelon: "🍉",
+    tangerine: "🍊",
+    orange: "🍊",
+    mandarin: "🍊",
+    lemon: "🍋",
+    banana: "🍌",
+    pineapple: "🍍",
+    mango: "🥭",
+    apple: "🍎",
+    green_apple: "🍏",
+    pear: "🍐",
+    peach: "🍑",
+    cherries: "🍒",
+    strawberry: "🍓",
+    blueberries: "🫐",
+    kiwi_fruit: "🥝",
+    tomato: "🍅",
+    olive: "🫒",
+    coconut: "🥥",
+    avocado: "🥑",
+    eggplant: "🍆",
+    potato: "🥔",
+    carrot: "🥕",
+    corn: "🌽",
+    hot_pepper: "🌶️",
+    bell_pepper: "🫑",
+    cucumber: "🥒",
+    leafy_green: "🥬",
+    broccoli: "🥦",
+    garlic: "🧄",
+    onion: "🧅",
+    mushroom: "🍄",
+    peanuts: "🥜",
+    chestnut: "🌰",
+    bread: "🍞",
+    croissant: "🥐",
+    baguette_bread: "🥖",
+    flatbread: "🫓",
+    pretzel: "🥨",
+    bagel: "🥯",
+    pancakes: "🥞",
+    waffle: "🧇",
+    cheese: "🧀",
+    meat_on_bone: "🍖",
+    poultry_leg: "🍗",
+    cut_of_meat: "🥩",
+    bacon: "🥓",
+    hamburger: "🍔",
+    fries: "🍟",
+    pizza: "🍕",
+    hotdog: "🌭",
+    sandwich: "🥪",
+    taco: "🌮",
+    burrito: "🌯",
+    tamale: "🫔",
+    stuffed_flatbread: "🥙",
+    falafel: "🧆",
+    egg: "🥚",
+    fried_egg: "🍳",
+    shallow_pan_of_food: "🥘",
+    stew: "🍲",
+    fondue: "🫕",
+    bowl_with_spoon: "🥣",
+    green_salad: "🥗",
+    popcorn: "🍿",
+    butter: "🧈",
+    salt: "🧂",
+    canned_food: "🥫",
+    bento: "🍱",
+    rice_cracker: "🍘",
+    rice_ball: "🍙",
+    rice: "🍚",
+    curry: "🍛",
+    ramen: "🍜",
+    spaghetti: "🍝",
+    sweet_potato: "🍠",
+    oden: "🍢",
+    sushi: "🍣",
+    fried_shrimp: "🍤",
+    fish_cake: "🍥",
+    moon_cake: "🥮",
+    dango: "🍡",
+    dumpling: "🥟",
+    fortune_cookie: "🥠",
+    takeout_box: "🥡",
+    crab: "🦀",
+    lobster: "🦞",
+    shrimp: "🦐",
+    squid: "🦑",
+    oyster: "🦪",
+    icecream: "🍦",
+    shaved_ice: "🍧",
+    ice_cream: "🍨",
+    doughnut: "🍩",
+    cookie: "🍪",
+    birthday: "🎂",
+    cake: "🍰",
+    cupcake: "🧁",
+    pie: "🥧",
+    chocolate_bar: "🍫",
+    candy: "🍬",
+    lollipop: "🍭",
+    custard: "🍮",
+    honey_pot: "🍯",
+    baby_bottle: "🍼",
+    milk_glass: "🥛",
+    coffee: "☕",
+    teapot: "🫖",
+    tea: "🍵",
+    sake: "🍶",
+    champagne: "🍾",
+    wine_glass: "🍷",
+    cocktail: "🍸",
+    tropical_drink: "🍹",
+    beer: "🍺",
+    beers: "🍻",
+    clinking_glasses: "🥂",
+    tumbler_glass: "🥃",
+    cup_with_straw: "🥤",
+    bubble_tea: "🧋",
+    beverage_box: "🧃",
+    mate: "🧉",
+    ice_cube: "🧊",
+    chopsticks: "🥢",
+    plate_with_cutlery: "🍽️",
+    fork_and_knife: "🍴",
+    spoon: "🥄",
+    hocho: "🔪",
+    knife: "🔪",
+    amphora: "🏺",
+    earth_africa: "🌍",
+    earth_americas: "🌎",
+    earth_asia: "🌏",
+    globe_with_meridians: "🌐",
+    world_map: "🗺️",
+    japan: "🗾",
+    compass: "🧭",
+    mountain_snow: "🏔️",
+    mountain: "⛰️",
+    volcano: "🌋",
+    mount_fuji: "🗻",
+    camping: "🏕️",
+    beach_umbrella: "🏖️",
+    desert: "🏜️",
+    desert_island: "🏝️",
+    national_park: "🏞️",
+    stadium: "🏟️",
+    classical_building: "🏛️",
+    building_construction: "🏗️",
+    bricks: "🧱",
+    rock: "🪨",
+    wood: "🪵",
+    hut: "🛖",
+    houses: "🏘️",
+    derelict_house: "🏚️",
+    house: "🏠",
+    house_with_garden: "🏡",
+    office: "🏢",
+    post_office: "🏣",
+    european_post_office: "🏤",
+    hospital: "🏥",
+    bank: "🏦",
+    hotel: "🏨",
+    love_hotel: "🏩",
+    convenience_store: "🏪",
+    school: "🏫",
+    department_store: "🏬",
+    factory: "🏭",
+    japanese_castle: "🏯",
+    european_castle: "🏰",
+    wedding: "💒",
+    tokyo_tower: "🗼",
+    statue_of_liberty: "🗽",
+    church: "⛪",
+    mosque: "🕌",
+    hindu_temple: "🛕",
+    synagogue: "🕍",
+    shinto_shrine: "⛩️",
+    kaaba: "🕋",
+    fountain: "⛲",
+    tent: "⛺",
+    foggy: "🌁",
+    night_with_stars: "🌃",
+    cityscape: "🏙️",
+    sunrise_over_mountains: "🌄",
+    sunrise: "🌅",
+    city_sunset: "🌆",
+    city_sunrise: "🌇",
+    bridge_at_night: "🌉",
+    hotsprings: "♨️",
+    carousel_horse: "🎠",
+    ferris_wheel: "🎡",
+    roller_coaster: "🎢",
+    barber: "💈",
+    circus_tent: "🎪",
+    steam_locomotive: "🚂",
+    railway_car: "🚃",
+    bullettrain_side: "🚄",
+    bullettrain_front: "🚅",
+    train2: "🚆",
+    metro: "🚇",
+    light_rail: "🚈",
+    station: "🚉",
+    tram: "🚊",
+    monorail: "🚝",
+    mountain_railway: "🚞",
+    train: "🚋",
+    bus: "🚌",
+    oncoming_bus: "🚍",
+    trolleybus: "🚎",
+    minibus: "🚐",
+    ambulance: "🚑",
+    fire_engine: "🚒",
+    police_car: "🚓",
+    oncoming_police_car: "🚔",
+    taxi: "🚕",
+    oncoming_taxi: "🚖",
+    car: "🚗",
+    red_car: "🚗",
+    oncoming_automobile: "🚘",
+    blue_car: "🚙",
+    pickup_truck: "🛻",
+    truck: "🚚",
+    articulated_lorry: "🚛",
+    tractor: "🚜",
+    racing_car: "🏎️",
+    motorcycle: "🏍️",
+    motor_scooter: "🛵",
+    manual_wheelchair: "🦽",
+    motorized_wheelchair: "🦼",
+    auto_rickshaw: "🛺",
+    bike: "🚲",
+    kick_scooter: "🛴",
+    skateboard: "🛹",
+    roller_skate: "🛼",
+    busstop: "🚏",
+    motorway: "🛣️",
+    railway_track: "🛤️",
+    oil_drum: "🛢️",
+    fuelpump: "⛽",
+    rotating_light: "🚨",
+    traffic_light: "🚥",
+    vertical_traffic_light: "🚦",
+    stop_sign: "🛑",
+    construction: "🚧",
+    anchor: "âš“",
+    boat: "⛵",
+    sailboat: "⛵",
+    canoe: "🛶",
+    speedboat: "🚤",
+    passenger_ship: "🛳️",
+    ferry: "⛴️",
+    motor_boat: "🛥️",
+    ship: "🚢",
+    airplane: "✈️",
+    small_airplane: "🛩️",
+    flight_departure: "🛫",
+    flight_arrival: "🛬",
+    parachute: "🪂",
+    seat: "💺",
+    helicopter: "🚁",
+    suspension_railway: "🚟",
+    mountain_cableway: "🚠",
+    aerial_tramway: "🚡",
+    artificial_satellite: "🛰️",
+    rocket: "🚀",
+    flying_saucer: "🛸",
+    bellhop_bell: "🛎️",
+    luggage: "🧳",
+    hourglass: "⌛",
+    hourglass_flowing_sand: "⏳",
+    watch: "⌚",
+    alarm_clock: "⏰",
+    stopwatch: "⏱️",
+    timer_clock: "⏲️",
+    mantelpiece_clock: "🕰️",
+    clock12: "🕛",
+    clock1230: "🕧",
+    clock1: "🕐",
+    clock130: "🕜",
+    clock2: "🕑",
+    clock230: "🕝",
+    clock3: "🕒",
+    clock330: "🕞",
+    clock4: "🕓",
+    clock430: "🕟",
+    clock5: "🕔",
+    clock530: "🕠",
+    clock6: "🕕",
+    clock630: "🕡",
+    clock7: "🕖",
+    clock730: "🕢",
+    clock8: "🕗",
+    clock830: "🕣",
+    clock9: "🕘",
+    clock930: "🕤",
+    clock10: "🕙",
+    clock1030: "🕥",
+    clock11: "🕚",
+    clock1130: "🕦",
+    new_moon: "🌑",
+    waxing_crescent_moon: "🌒",
+    first_quarter_moon: "🌓",
+    moon: "🌔",
+    waxing_gibbous_moon: "🌔",
+    full_moon: "🌕",
+    waning_gibbous_moon: "🌖",
+    last_quarter_moon: "🌗",
+    waning_crescent_moon: "🌘",
+    crescent_moon: "🌙",
+    new_moon_with_face: "🌚",
+    first_quarter_moon_with_face: "🌛",
+    last_quarter_moon_with_face: "🌜",
+    thermometer: "🌡️",
+    sunny: "☀️",
+    full_moon_with_face: "🌝",
+    sun_with_face: "🌞",
+    ringed_planet: "🪐",
+    star: "⭐",
+    star2: "🌟",
+    stars: "🌠",
+    milky_way: "🌌",
+    cloud: "☁️",
+    partly_sunny: "â›…",
+    cloud_with_lightning_and_rain: "⛈️",
+    sun_behind_small_cloud: "🌤️",
+    sun_behind_large_cloud: "🌥️",
+    sun_behind_rain_cloud: "🌦️",
+    cloud_with_rain: "🌧️",
+    cloud_with_snow: "🌨️",
+    cloud_with_lightning: "🌩️",
+    tornado: "🌪️",
+    fog: "🌫️",
+    wind_face: "🌬️",
+    cyclone: "🌀",
+    rainbow: "🌈",
+    closed_umbrella: "🌂",
+    open_umbrella: "☂️",
+    umbrella: "☔",
+    parasol_on_ground: "⛱️",
+    zap: "âš¡",
+    snowflake: "❄️",
+    snowman_with_snow: "☃️",
+    snowman: "⛄",
+    comet: "☄️",
+    fire: "🔥",
+    droplet: "💧",
+    ocean: "🌊",
+    jack_o_lantern: "🎃",
+    christmas_tree: "🎄",
+    fireworks: "🎆",
+    sparkler: "🎇",
+    firecracker: "🧨",
+    sparkles: "✨",
+    balloon: "🎈",
+    tada: "🎉",
+    confetti_ball: "🎊",
+    tanabata_tree: "🎋",
+    bamboo: "🎍",
+    dolls: "🎎",
+    flags: "🎏",
+    wind_chime: "🎐",
+    rice_scene: "🎑",
+    red_envelope: "🧧",
+    ribbon: "🎀",
+    gift: "🎁",
+    reminder_ribbon: "🎗️",
+    tickets: "🎟️",
+    ticket: "🎫",
+    medal_military: "🎖️",
+    trophy: "🏆",
+    medal_sports: "🏅",
+    "1st_place_medal": "🥇",
+    "2nd_place_medal": "🥈",
+    "3rd_place_medal": "🥉",
+    soccer: "âš½",
+    baseball: "âš¾",
+    softball: "🥎",
+    basketball: "🏀",
+    volleyball: "🏐",
+    football: "🏈",
+    rugby_football: "🏉",
+    tennis: "🎾",
+    flying_disc: "🥏",
+    bowling: "🎳",
+    cricket_game: "🏏",
+    field_hockey: "🏑",
+    ice_hockey: "🏒",
+    lacrosse: "🥍",
+    ping_pong: "🏓",
+    badminton: "🏸",
+    boxing_glove: "🥊",
+    martial_arts_uniform: "🥋",
+    goal_net: "🥅",
+    golf: "⛳",
+    ice_skate: "⛸️",
+    fishing_pole_and_fish: "🎣",
+    diving_mask: "🤿",
+    running_shirt_with_sash: "🎽",
+    ski: "🎿",
+    sled: "🛷",
+    curling_stone: "🥌",
+    dart: "🎯",
+    yo_yo: "🪀",
+    kite: "🪁",
+    "8ball": "🎱",
+    crystal_ball: "🔮",
+    magic_wand: "🪄",
+    nazar_amulet: "🧿",
+    video_game: "🎮",
+    joystick: "🕹️",
+    slot_machine: "🎰",
+    game_die: "🎲",
+    jigsaw: "🧩",
+    teddy_bear: "🧸",
+    pinata: "🪅",
+    nesting_dolls: "🪆",
+    spades: "♠️",
+    hearts: "♥️",
+    diamonds: "♦️",
+    clubs: "♣️",
+    chess_pawn: "♟️",
+    black_joker: "🃏",
+    mahjong: "🀄",
+    flower_playing_cards: "🎴",
+    performing_arts: "🎭",
+    framed_picture: "🖼️",
+    art: "🎨",
+    thread: "🧵",
+    sewing_needle: "🪡",
+    yarn: "🧶",
+    knot: "🪢",
+    eyeglasses: "👓",
+    dark_sunglasses: "🕶️",
+    goggles: "🥽",
+    lab_coat: "🥼",
+    safety_vest: "🦺",
+    necktie: "👔",
+    shirt: "👕",
+    tshirt: "👕",
+    jeans: "👖",
+    scarf: "🧣",
+    gloves: "🧤",
+    coat: "🧥",
+    socks: "🧦",
+    dress: "👗",
+    kimono: "👘",
+    sari: "🥻",
+    one_piece_swimsuit: "🩱",
+    swim_brief: "🩲",
+    shorts: "🩳",
+    bikini: "👙",
+    womans_clothes: "👚",
+    purse: "👛",
+    handbag: "👜",
+    pouch: "👝",
+    shopping: "🛍️",
+    school_satchel: "🎒",
+    thong_sandal: "🩴",
+    mans_shoe: "👞",
+    shoe: "👞",
+    athletic_shoe: "👟",
+    hiking_boot: "🥾",
+    flat_shoe: "🥿",
+    high_heel: "👠",
+    sandal: "👡",
+    ballet_shoes: "🩰",
+    boot: "👢",
+    crown: "👑",
+    womans_hat: "👒",
+    tophat: "🎩",
+    mortar_board: "🎓",
+    billed_cap: "🧢",
+    military_helmet: "🪖",
+    rescue_worker_helmet: "⛑️",
+    prayer_beads: "📿",
+    lipstick: "💄",
+    ring: "💍",
+    gem: "💎",
+    mute: "🔇",
+    speaker: "🔈",
+    sound: "🔉",
+    loud_sound: "🔊",
+    loudspeaker: "📢",
+    mega: "📣",
+    postal_horn: "📯",
+    bell: "🔔",
+    no_bell: "🔕",
+    musical_score: "🎼",
+    musical_note: "🎵",
+    notes: "🎶",
+    studio_microphone: "🎙️",
+    level_slider: "🎚️",
+    control_knobs: "🎛️",
+    microphone: "🎤",
+    headphones: "🎧",
+    radio: "📻",
+    saxophone: "🎷",
+    accordion: "🪗",
+    guitar: "🎸",
+    musical_keyboard: "🎹",
+    trumpet: "🎺",
+    violin: "🎻",
+    banjo: "🪕",
+    drum: "🥁",
+    long_drum: "🪘",
+    iphone: "📱",
+    calling: "📲",
+    phone: "☎️",
+    telephone: "☎️",
+    telephone_receiver: "📞",
+    pager: "📟",
+    fax: "📠",
+    battery: "🔋",
+    electric_plug: "🔌",
+    computer: "💻",
+    desktop_computer: "🖥️",
+    printer: "🖨️",
+    keyboard: "⌨️",
+    computer_mouse: "🖱️",
+    trackball: "🖲️",
+    minidisc: "💽",
+    floppy_disk: "💾",
+    cd: "💿",
+    dvd: "📀",
+    abacus: "🧮",
+    movie_camera: "🎥",
+    film_strip: "🎞️",
+    film_projector: "📽️",
+    clapper: "🎬",
+    tv: "📺",
+    camera: "📷",
+    camera_flash: "📸",
+    video_camera: "📹",
+    vhs: "📼",
+    mag: "🔍",
+    mag_right: "🔎",
+    candle: "🕯️",
+    bulb: "💡",
+    flashlight: "🔦",
+    izakaya_lantern: "🏮",
+    lantern: "🏮",
+    diya_lamp: "🪔",
+    notebook_with_decorative_cover: "📔",
+    closed_book: "📕",
+    book: "📖",
+    open_book: "📖",
+    green_book: "📗",
+    blue_book: "📘",
+    orange_book: "📙",
+    books: "📚",
+    notebook: "📓",
+    ledger: "📒",
+    page_with_curl: "📃",
+    scroll: "📜",
+    page_facing_up: "📄",
+    newspaper: "📰",
+    newspaper_roll: "🗞️",
+    bookmark_tabs: "📑",
+    bookmark: "🔖",
+    label: "🏷️",
+    moneybag: "💰",
+    coin: "🪙",
+    yen: "💴",
+    dollar: "💵",
+    euro: "💶",
+    pound: "💷",
+    money_with_wings: "💸",
+    credit_card: "💳",
+    receipt: "🧾",
+    chart: "💹",
+    envelope: "✉️",
+    email: "📧",
+    "e-mail": "📧",
+    incoming_envelope: "📨",
+    envelope_with_arrow: "📩",
+    outbox_tray: "📤",
+    inbox_tray: "📥",
+    package: "📦",
+    mailbox: "📫",
+    mailbox_closed: "📪",
+    mailbox_with_mail: "📬",
+    mailbox_with_no_mail: "📭",
+    postbox: "📮",
+    ballot_box: "🗳️",
+    pencil2: "✏️",
+    black_nib: "✒️",
+    fountain_pen: "🖋️",
+    pen: "🖊️",
+    paintbrush: "🖌️",
+    crayon: "🖍️",
+    memo: "📝",
+    pencil: "📝",
+    briefcase: "💼",
+    file_folder: "📁",
+    open_file_folder: "📂",
+    card_index_dividers: "🗂️",
+    date: "📅",
+    calendar: "📆",
+    spiral_notepad: "🗒️",
+    spiral_calendar: "🗓️",
+    card_index: "📇",
+    chart_with_upwards_trend: "📈",
+    chart_with_downwards_trend: "📉",
+    bar_chart: "📊",
+    clipboard: "📋",
+    pushpin: "📌",
+    round_pushpin: "📍",
+    paperclip: "📎",
+    paperclips: "🖇️",
+    straight_ruler: "📏",
+    triangular_ruler: "📐",
+    scissors: "✂️",
+    card_file_box: "🗃️",
+    file_cabinet: "🗄️",
+    wastebasket: "🗑️",
+    lock: "🔒",
+    unlock: "🔓",
+    lock_with_ink_pen: "🔏",
+    closed_lock_with_key: "🔐",
+    key: "🔑",
+    old_key: "🗝️",
+    hammer: "🔨",
+    axe: "🪓",
+    pick: "⛏️",
+    hammer_and_pick: "⚒️",
+    hammer_and_wrench: "🛠️",
+    dagger: "🗡️",
+    crossed_swords: "⚔️",
+    gun: "🔫",
+    boomerang: "🪃",
+    bow_and_arrow: "🏹",
+    shield: "🛡️",
+    carpentry_saw: "🪚",
+    wrench: "🔧",
+    screwdriver: "🪛",
+    nut_and_bolt: "🔩",
+    gear: "⚙️",
+    clamp: "🗜️",
+    balance_scale: "⚖️",
+    probing_cane: "🦯",
+    link: "🔗",
+    chains: "⛓️",
+    hook: "🪝",
+    toolbox: "🧰",
+    magnet: "🧲",
+    ladder: "🪜",
+    alembic: "⚗️",
+    test_tube: "🧪",
+    petri_dish: "🧫",
+    dna: "🧬",
+    microscope: "🔬",
+    telescope: "🔭",
+    satellite: "📡",
+    syringe: "💉",
+    drop_of_blood: "🩸",
+    pill: "💊",
+    adhesive_bandage: "🩹",
+    stethoscope: "🩺",
+    door: "🚪",
+    elevator: "🛗",
+    mirror: "🪞",
+    window: "🪟",
+    bed: "🛏️",
+    couch_and_lamp: "🛋️",
+    chair: "🪑",
+    toilet: "🚽",
+    plunger: "🪠",
+    shower: "🚿",
+    bathtub: "🛁",
+    mouse_trap: "🪤",
+    razor: "🪒",
+    lotion_bottle: "🧴",
+    safety_pin: "🧷",
+    broom: "🧹",
+    basket: "🧺",
+    roll_of_paper: "🧻",
+    bucket: "🪣",
+    soap: "🧼",
+    toothbrush: "🪥",
+    sponge: "🧽",
+    fire_extinguisher: "🧯",
+    shopping_cart: "🛒",
+    smoking: "🚬",
+    coffin: "⚰️",
+    headstone: "🪦",
+    funeral_urn: "⚱️",
+    moyai: "🗿",
+    placard: "🪧",
+    atm: "🏧",
+    put_litter_in_its_place: "🚮",
+    potable_water: "🚰",
+    wheelchair: "♿",
+    mens: "🚹",
+    womens: "🚺",
+    restroom: "🚻",
+    baby_symbol: "🚼",
+    wc: "🚾",
+    passport_control: "🛂",
+    customs: "🛃",
+    baggage_claim: "🛄",
+    left_luggage: "🛅",
+    warning: "⚠️",
+    children_crossing: "🚸",
+    no_entry: "â›”",
+    no_entry_sign: "🚫",
+    no_bicycles: "🚳",
+    no_smoking: "🚭",
+    do_not_litter: "🚯",
+    "non-potable_water": "🚱",
+    no_pedestrians: "🚷",
+    no_mobile_phones: "📵",
+    underage: "🔞",
+    radioactive: "☢️",
+    biohazard: "☣️",
+    arrow_up: "⬆️",
+    arrow_upper_right: "↗️",
+    arrow_right: "➡️",
+    arrow_lower_right: "↘️",
+    arrow_down: "⬇️",
+    arrow_lower_left: "↙️",
+    arrow_left: "⬅️",
+    arrow_upper_left: "↖️",
+    arrow_up_down: "↕️",
+    left_right_arrow: "↔️",
+    leftwards_arrow_with_hook: "↩️",
+    arrow_right_hook: "↪️",
+    arrow_heading_up: "⤴️",
+    arrow_heading_down: "⤵️",
+    arrows_clockwise: "🔃",
+    arrows_counterclockwise: "🔄",
+    back: "🔙",
+    end: "🔚",
+    on: "🔛",
+    soon: "🔜",
+    top: "🔝",
+    place_of_worship: "🛐",
+    atom_symbol: "⚛️",
+    om: "🕉️",
+    star_of_david: "✡️",
+    wheel_of_dharma: "☸️",
+    yin_yang: "☯️",
+    latin_cross: "✝️",
+    orthodox_cross: "☦️",
+    star_and_crescent: "☪️",
+    peace_symbol: "☮️",
+    menorah: "🕎",
+    six_pointed_star: "🔯",
+    aries: "♈",
+    taurus: "♉",
+    gemini: "♊",
+    cancer: "♋",
+    leo: "♌",
+    virgo: "♍",
+    libra: "♎",
+    scorpius: "♏",
+    sagittarius: "♐",
+    capricorn: "♑",
+    aquarius: "â™’",
+    pisces: "♓",
+    ophiuchus: "⛎",
+    twisted_rightwards_arrows: "🔀",
+    repeat: "🔁",
+    repeat_one: "🔂",
+    arrow_forward: "▶️",
+    fast_forward: "⏩",
+    next_track_button: "⏭️",
+    play_or_pause_button: "⏯️",
+    arrow_backward: "◀️",
+    rewind: "⏪",
+    previous_track_button: "⏮️",
+    arrow_up_small: "🔼",
+    arrow_double_up: "⏫",
+    arrow_down_small: "🔽",
+    arrow_double_down: "⏬",
+    pause_button: "⏸️",
+    stop_button: "⏹️",
+    record_button: "⏺️",
+    eject_button: "⏏️",
+    cinema: "🎦",
+    low_brightness: "🔅",
+    high_brightness: "🔆",
+    signal_strength: "📶",
+    vibration_mode: "📳",
+    mobile_phone_off: "📴",
+    female_sign: "♀️",
+    male_sign: "♂️",
+    transgender_symbol: "⚧️",
+    heavy_multiplication_x: "✖️",
+    heavy_plus_sign: "âž•",
+    heavy_minus_sign: "âž–",
+    heavy_division_sign: "âž—",
+    infinity: "♾️",
+    bangbang: "‼️",
+    interrobang: "⁉️",
+    question: "❓",
+    grey_question: "❔",
+    grey_exclamation: "❕",
+    exclamation: "❗",
+    heavy_exclamation_mark: "❗",
+    wavy_dash: "〰️",
+    currency_exchange: "💱",
+    heavy_dollar_sign: "💲",
+    medical_symbol: "⚕️",
+    recycle: "♻️",
+    fleur_de_lis: "⚜️",
+    trident: "🔱",
+    name_badge: "📛",
+    beginner: "🔰",
+    o: "â­•",
+    white_check_mark: "✅",
+    ballot_box_with_check: "☑️",
+    heavy_check_mark: "✔️",
+    x: "❌",
+    negative_squared_cross_mark: "❎",
+    curly_loop: "âž°",
+    loop: "âž¿",
+    part_alternation_mark: "〽️",
+    eight_spoked_asterisk: "✳️",
+    eight_pointed_black_star: "✴️",
+    sparkle: "❇️",
+    copyright: "©️",
+    registered: "®️",
+    tm: "™️",
+    hash: "#️⃣",
+    asterisk: "*️⃣",
+    zero: "0️⃣",
+    one: "1️⃣",
+    two: "2️⃣",
+    three: "3️⃣",
+    four: "4️⃣",
+    five: "5️⃣",
+    six: "6️⃣",
+    seven: "7️⃣",
+    eight: "8️⃣",
+    nine: "9️⃣",
+    keycap_ten: "🔟",
+    capital_abcd: "🔠",
+    abcd: "🔡",
+    symbols: "🔣",
+    abc: "🔤",
+    a: "🅰️",
+    ab: "🆎",
+    b: "🅱️",
+    cl: "🆑",
+    cool: "🆒",
+    free: "🆓",
+    information_source: "ℹ️",
+    id: "🆔",
+    m: "Ⓜ️",
+    new: "🆕",
+    ng: "🆖",
+    o2: "🅾️",
+    ok: "🆗",
+    parking: "🅿️",
+    sos: "🆘",
+    up: "🆙",
+    vs: "🆚",
+    koko: "🈁",
+    sa: "🈂️",
+    u6708: "🈷️",
+    u6709: "🈶",
+    u6307: "🈯",
+    ideograph_advantage: "🉐",
+    u5272: "🈹",
+    u7121: "🈚",
+    u7981: "🈲",
+    accept: "🉑",
+    u7533: "🈸",
+    u5408: "🈴",
+    u7a7a: "🈳",
+    congratulations: "㊗️",
+    secret: "㊙️",
+    u55b6: "🈺",
+    u6e80: "🈵",
+    red_circle: "🔴",
+    orange_circle: "🟠",
+    yellow_circle: "🟡",
+    green_circle: "🟢",
+    large_blue_circle: "🔵",
+    purple_circle: "🟣",
+    brown_circle: "🟤",
+    black_circle: "âš«",
+    white_circle: "⚪",
+    red_square: "🟥",
+    orange_square: "🟧",
+    yellow_square: "🟨",
+    green_square: "🟩",
+    blue_square: "🟦",
+    purple_square: "🟪",
+    brown_square: "🟫",
+    black_large_square: "⬛",
+    white_large_square: "⬜",
+    black_medium_square: "◼️",
+    white_medium_square: "◻️",
+    black_medium_small_square: "â—¾",
+    white_medium_small_square: "â—½",
+    black_small_square: "▪️",
+    white_small_square: "▫️",
+    large_orange_diamond: "🔶",
+    large_blue_diamond: "🔷",
+    small_orange_diamond: "🔸",
+    small_blue_diamond: "🔹",
+    small_red_triangle: "🔺",
+    small_red_triangle_down: "🔻",
+    diamond_shape_with_a_dot_inside: "💠",
+    radio_button: "🔘",
+    white_square_button: "🔳",
+    black_square_button: "🔲",
+    checkered_flag: "🏁",
+    triangular_flag_on_post: "🚩",
+    crossed_flags: "🎌",
+    black_flag: "🏴",
+    white_flag: "🏳️",
+    rainbow_flag: "🏳️‍🌈",
+    transgender_flag: "🏳️‍⚧️",
+    pirate_flag: "🏴‍☠️",
+    ascension_island: "🇦🇨",
+    andorra: "🇦🇩",
+    united_arab_emirates: "🇦🇪",
+    afghanistan: "🇦🇫",
+    antigua_barbuda: "🇦🇬",
+    anguilla: "🇦🇮",
+    albania: "🇦🇱",
+    armenia: "🇦🇲",
+    angola: "🇦🇴",
+    antarctica: "🇦🇶",
+    argentina: "🇦🇷",
+    american_samoa: "🇦🇸",
+    austria: "🇦🇹",
+    australia: "🇦🇺",
+    aruba: "🇦🇼",
+    aland_islands: "🇦🇽",
+    azerbaijan: "🇦🇿",
+    bosnia_herzegovina: "🇧🇦",
+    barbados: "🇧🇧",
+    bangladesh: "🇧🇩",
+    belgium: "🇧🇪",
+    burkina_faso: "🇧🇫",
+    bulgaria: "🇧🇬",
+    bahrain: "🇧🇭",
+    burundi: "🇧🇮",
+    benin: "🇧🇯",
+    st_barthelemy: "🇧🇱",
+    bermuda: "🇧🇲",
+    brunei: "🇧🇳",
+    bolivia: "🇧🇴",
+    caribbean_netherlands: "🇧🇶",
+    brazil: "🇧🇷",
+    bahamas: "🇧🇸",
+    bhutan: "🇧🇹",
+    bouvet_island: "🇧🇻",
+    botswana: "🇧🇼",
+    belarus: "🇧🇾",
+    belize: "🇧🇿",
+    canada: "🇨🇦",
+    cocos_islands: "🇨🇨",
+    congo_kinshasa: "🇨🇩",
+    central_african_republic: "🇨🇫",
+    congo_brazzaville: "🇨🇬",
+    switzerland: "🇨🇭",
+    cote_divoire: "🇨🇮",
+    cook_islands: "🇨🇰",
+    chile: "🇨🇱",
+    cameroon: "🇨🇲",
+    cn: "🇨🇳",
+    colombia: "🇨🇴",
+    clipperton_island: "🇨🇵",
+    costa_rica: "🇨🇷",
+    cuba: "🇨🇺",
+    cape_verde: "🇨🇻",
+    curacao: "🇨🇼",
+    christmas_island: "🇨🇽",
+    cyprus: "🇨🇾",
+    czech_republic: "🇨🇿",
+    de: "🇩🇪",
+    diego_garcia: "🇩🇬",
+    djibouti: "🇩🇯",
+    denmark: "🇩🇰",
+    dominica: "🇩🇲",
+    dominican_republic: "🇩🇴",
+    algeria: "🇩🇿",
+    ceuta_melilla: "🇪🇦",
+    ecuador: "🇪🇨",
+    estonia: "🇪🇪",
+    egypt: "🇪🇬",
+    western_sahara: "🇪🇭",
+    eritrea: "🇪🇷",
+    es: "🇪🇸",
+    ethiopia: "🇪🇹",
+    eu: "🇪🇺",
+    european_union: "🇪🇺",
+    finland: "🇫🇮",
+    fiji: "🇫🇯",
+    falkland_islands: "🇫🇰",
+    micronesia: "🇫🇲",
+    faroe_islands: "🇫🇴",
+    fr: "🇫🇷",
+    gabon: "🇬🇦",
+    gb: "🇬🇧",
+    uk: "🇬🇧",
+    grenada: "🇬🇩",
+    georgia: "🇬🇪",
+    french_guiana: "🇬🇫",
+    guernsey: "🇬🇬",
+    ghana: "🇬🇭",
+    gibraltar: "🇬🇮",
+    greenland: "🇬🇱",
+    gambia: "🇬🇲",
+    guinea: "🇬🇳",
+    guadeloupe: "🇬🇵",
+    equatorial_guinea: "🇬🇶",
+    greece: "🇬🇷",
+    south_georgia_south_sandwich_islands: "🇬🇸",
+    guatemala: "🇬🇹",
+    guam: "🇬🇺",
+    guinea_bissau: "🇬🇼",
+    guyana: "🇬🇾",
+    hong_kong: "🇭🇰",
+    heard_mcdonald_islands: "🇭🇲",
+    honduras: "🇭🇳",
+    croatia: "🇭🇷",
+    haiti: "🇭🇹",
+    hungary: "🇭🇺",
+    canary_islands: "🇮🇨",
+    indonesia: "🇮🇩",
+    ireland: "🇮🇪",
+    israel: "🇮🇱",
+    isle_of_man: "🇮🇲",
+    india: "🇮🇳",
+    british_indian_ocean_territory: "🇮🇴",
+    iraq: "🇮🇶",
+    iran: "🇮🇷",
+    iceland: "🇮🇸",
+    it: "🇮🇹",
+    jersey: "🇯🇪",
+    jamaica: "🇯🇲",
+    jordan: "🇯🇴",
+    jp: "🇯🇵",
+    kenya: "🇰🇪",
+    kyrgyzstan: "🇰🇬",
+    cambodia: "🇰🇭",
+    kiribati: "🇰🇮",
+    comoros: "🇰🇲",
+    st_kitts_nevis: "🇰🇳",
+    north_korea: "🇰🇵",
+    kr: "🇰🇷",
+    kuwait: "🇰🇼",
+    cayman_islands: "🇰🇾",
+    kazakhstan: "🇰🇿",
+    laos: "🇱🇦",
+    lebanon: "🇱🇧",
+    st_lucia: "🇱🇨",
+    liechtenstein: "🇱🇮",
+    sri_lanka: "🇱🇰",
+    liberia: "🇱🇷",
+    lesotho: "🇱🇸",
+    lithuania: "🇱🇹",
+    luxembourg: "🇱🇺",
+    latvia: "🇱🇻",
+    libya: "🇱🇾",
+    morocco: "🇲🇦",
+    monaco: "🇲🇨",
+    moldova: "🇲🇩",
+    montenegro: "🇲🇪",
+    st_martin: "🇲🇫",
+    madagascar: "🇲🇬",
+    marshall_islands: "🇲🇭",
+    macedonia: "🇲🇰",
+    mali: "🇲🇱",
+    myanmar: "🇲🇲",
+    mongolia: "🇲🇳",
+    macau: "🇲🇴",
+    northern_mariana_islands: "🇲🇵",
+    martinique: "🇲🇶",
+    mauritania: "🇲🇷",
+    montserrat: "🇲🇸",
+    malta: "🇲🇹",
+    mauritius: "🇲🇺",
+    maldives: "🇲🇻",
+    malawi: "🇲🇼",
+    mexico: "🇲🇽",
+    malaysia: "🇲🇾",
+    mozambique: "🇲🇿",
+    namibia: "🇳🇦",
+    new_caledonia: "🇳🇨",
+    niger: "🇳🇪",
+    norfolk_island: "🇳🇫",
+    nigeria: "🇳🇬",
+    nicaragua: "🇳🇮",
+    netherlands: "🇳🇱",
+    norway: "🇳🇴",
+    nepal: "🇳🇵",
+    nauru: "🇳🇷",
+    niue: "🇳🇺",
+    new_zealand: "🇳🇿",
+    oman: "🇴🇲",
+    panama: "🇵🇦",
+    peru: "🇵🇪",
+    french_polynesia: "🇵🇫",
+    papua_new_guinea: "🇵🇬",
+    philippines: "🇵🇭",
+    pakistan: "🇵🇰",
+    poland: "🇵🇱",
+    st_pierre_miquelon: "🇵🇲",
+    pitcairn_islands: "🇵🇳",
+    puerto_rico: "🇵🇷",
+    palestinian_territories: "🇵🇸",
+    portugal: "🇵🇹",
+    palau: "🇵🇼",
+    paraguay: "🇵🇾",
+    qatar: "🇶🇦",
+    reunion: "🇷🇪",
+    romania: "🇷🇴",
+    serbia: "🇷🇸",
+    ru: "🇷🇺",
+    rwanda: "🇷🇼",
+    saudi_arabia: "🇸🇦",
+    solomon_islands: "🇸🇧",
+    seychelles: "🇸🇨",
+    sudan: "🇸🇩",
+    sweden: "🇸🇪",
+    singapore: "🇸🇬",
+    st_helena: "🇸🇭",
+    slovenia: "🇸🇮",
+    svalbard_jan_mayen: "🇸🇯",
+    slovakia: "🇸🇰",
+    sierra_leone: "🇸🇱",
+    san_marino: "🇸🇲",
+    senegal: "🇸🇳",
+    somalia: "🇸🇴",
+    suriname: "🇸🇷",
+    south_sudan: "🇸🇸",
+    sao_tome_principe: "🇸🇹",
+    el_salvador: "🇸🇻",
+    sint_maarten: "🇸🇽",
+    syria: "🇸🇾",
+    swaziland: "🇸🇿",
+    tristan_da_cunha: "🇹🇦",
+    turks_caicos_islands: "🇹🇨",
+    chad: "🇹🇩",
+    french_southern_territories: "🇹🇫",
+    togo: "🇹🇬",
+    thailand: "🇹🇭",
+    tajikistan: "🇹🇯",
+    tokelau: "🇹🇰",
+    timor_leste: "🇹🇱",
+    turkmenistan: "🇹🇲",
+    tunisia: "🇹🇳",
+    tonga: "🇹🇴",
+    tr: "🇹🇷",
+    trinidad_tobago: "🇹🇹",
+    tuvalu: "🇹🇻",
+    taiwan: "🇹🇼",
+    tanzania: "🇹🇿",
+    ukraine: "🇺🇦",
+    uganda: "🇺🇬",
+    us_outlying_islands: "🇺🇲",
+    united_nations: "🇺🇳",
+    us: "🇺🇸",
+    uruguay: "🇺🇾",
+    uzbekistan: "🇺🇿",
+    vatican_city: "🇻🇦",
+    st_vincent_grenadines: "🇻🇨",
+    venezuela: "🇻🇪",
+    british_virgin_islands: "🇻🇬",
+    us_virgin_islands: "🇻🇮",
+    vietnam: "🇻🇳",
+    vanuatu: "🇻🇺",
+    wallis_futuna: "🇼🇫",
+    samoa: "🇼🇸",
+    kosovo: "🇽🇰",
+    yemen: "🇾🇪",
+    mayotte: "🇾🇹",
+    south_africa: "🇿🇦",
+    zambia: "🇿🇲",
+    zimbabwe: "🇿🇼",
+    england: "🏴󠁧󠁢󠁥󠁮󠁧󠁿",
+    scotland: "🏴󠁧󠁢󠁳󠁣󠁴󠁿",
+    wales: "🏴󠁧󠁢󠁷󠁬󠁳󠁿",
 };
diff --git a/src/assets/sounds/Audio.ts b/src/assets/sounds/Audio.ts
index be4881b..031e7b4 100644
--- a/src/assets/sounds/Audio.ts
+++ b/src/assets/sounds/Audio.ts
@@ -4,26 +4,26 @@ import message from "./message.mp3";
 import outbound from "./outbound.mp3";
 
 const SoundMap: { [key in Sounds]: string } = {
-	message,
-	outbound,
-	call_join,
-	call_leave,
+    message,
+    outbound,
+    call_join,
+    call_leave,
 };
 
 export type Sounds = "message" | "outbound" | "call_join" | "call_leave";
 export const SOUNDS_ARRAY: Sounds[] = [
-	"message",
-	"outbound",
-	"call_join",
-	"call_leave",
+    "message",
+    "outbound",
+    "call_join",
+    "call_leave",
 ];
 
 export function playSound(sound: Sounds) {
-	let file = SoundMap[sound];
-	let el = new Audio(file);
-	try {
-		el.play();
-	} catch (err) {
-		console.error("Failed to play audio file", file, err);
-	}
+    let file = SoundMap[sound];
+    let el = new Audio(file);
+    try {
+        el.play();
+    } catch (err) {
+        console.error("Failed to play audio file", file, err);
+    }
 }
diff --git a/src/components/common/AutoComplete.tsx b/src/components/common/AutoComplete.tsx
index 419a969..ea978e1 100644
--- a/src/components/common/AutoComplete.tsx
+++ b/src/components/common/AutoComplete.tsx
@@ -12,464 +12,464 @@ import Emoji from "./Emoji";
 import UserIcon from "./user/UserIcon";
 
 export type AutoCompleteState =
-	| { type: "none" }
-	| ({ selected: number; within: boolean } & (
-			| {
-					type: "emoji";
-					matches: string[];
-			  }
-			| {
-					type: "user";
-					matches: User[];
-			  }
-			| {
-					type: "channel";
-					matches: Channels.TextChannel[];
-			  }
-	  ));
+    | { type: "none" }
+    | ({ selected: number; within: boolean } & (
+          | {
+                type: "emoji";
+                matches: string[];
+            }
+          | {
+                type: "user";
+                matches: User[];
+            }
+          | {
+                type: "channel";
+                matches: Channels.TextChannel[];
+            }
+      ));
 
 export type SearchClues = {
-	users?: { type: "channel"; id: string } | { type: "all" };
-	channels?: { server: string };
+    users?: { type: "channel"; id: string } | { type: "all" };
+    channels?: { server: string };
 };
 
 export type AutoCompleteProps = {
-	detached?: boolean;
-	state: AutoCompleteState;
-	setState: StateUpdater<AutoCompleteState>;
-
-	onKeyUp: (ev: KeyboardEvent) => void;
-	onKeyDown: (ev: KeyboardEvent) => boolean;
-	onChange: (ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) => void;
-	onClick: JSX.MouseEventHandler<HTMLButtonElement>;
-	onFocus: JSX.FocusEventHandler<HTMLTextAreaElement>;
-	onBlur: JSX.FocusEventHandler<HTMLTextAreaElement>;
+    detached?: boolean;
+    state: AutoCompleteState;
+    setState: StateUpdater<AutoCompleteState>;
+
+    onKeyUp: (ev: KeyboardEvent) => void;
+    onKeyDown: (ev: KeyboardEvent) => boolean;
+    onChange: (ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) => void;
+    onClick: JSX.MouseEventHandler<HTMLButtonElement>;
+    onFocus: JSX.FocusEventHandler<HTMLTextAreaElement>;
+    onBlur: JSX.FocusEventHandler<HTMLTextAreaElement>;
 };
 
 export function useAutoComplete(
-	setValue: (v?: string) => void,
-	searchClues?: SearchClues,
+    setValue: (v?: string) => void,
+    searchClues?: SearchClues,
 ): AutoCompleteProps {
-	const [state, setState] = useState<AutoCompleteState>({ type: "none" });
-	const [focused, setFocused] = useState(false);
-	const client = useContext(AppContext);
-
-	function findSearchString(
-		el: HTMLTextAreaElement,
-	): ["emoji" | "user" | "channel", string, number] | undefined {
-		if (el.selectionStart === el.selectionEnd) {
-			let cursor = el.selectionStart;
-			let content = el.value.slice(0, cursor);
-
-			let valid = /\w/;
-
-			let j = content.length - 1;
-			if (content[j] === "@") {
-				return ["user", "", j];
-			} else if (content[j] === "#") {
-				return ["channel", "", j];
-			}
-
-			while (j >= 0 && valid.test(content[j])) {
-				j--;
-			}
-
-			if (j === -1) return;
-			let current = content[j];
-
-			if (current === ":" || current === "@" || current === "#") {
-				let search = content.slice(j + 1, content.length);
-				if (search.length > 0) {
-					return [
-						current === "#"
-							? "channel"
-							: current === ":"
-							? "emoji"
-							: "user",
-						search.toLowerCase(),
-						j + 1,
-					];
-				}
-			}
-		}
-	}
-
-	function onChange(ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) {
-		const el = ev.currentTarget;
-
-		let result = findSearchString(el);
-		if (result) {
-			let [type, search] = result;
-			const regex = new RegExp(search, "i");
-
-			if (type === "emoji") {
-				// ! FIXME: we should convert it to a Binary Search Tree and use that
-				let matches = Object.keys(emojiDictionary)
-					.filter((emoji: string) => emoji.match(regex))
-					.splice(0, 5);
-
-				if (matches.length > 0) {
-					let currentPosition =
-						state.type !== "none" ? state.selected : 0;
-
-					setState({
-						type: "emoji",
-						matches,
-						selected: Math.min(currentPosition, matches.length - 1),
-						within: false,
-					});
-
-					return;
-				}
-			}
-
-			if (type === "user" && searchClues?.users) {
-				let users: User[] = [];
-				switch (searchClues.users.type) {
-					case "all":
-						users = client.users.toArray();
-						break;
-					case "channel": {
-						let channel = client.channels.get(searchClues.users.id);
-						switch (channel?.channel_type) {
-							case "Group":
-							case "DirectMessage":
-								users = client.users
-									.mapKeys(channel.recipients)
-									.filter(
-										(x) => typeof x !== "undefined",
-									) as User[];
-								break;
-							case "TextChannel":
-								const server = channel.server;
-								users = client.servers.members
-									.toArray()
-									.filter(
-										(x) => x._id.substr(0, 26) === server,
-									)
-									.map((x) =>
-										client.users.get(x._id.substr(26)),
-									)
-									.filter(
-										(x) => typeof x !== "undefined",
-									) as User[];
-								break;
-							default:
-								return;
-						}
-					}
-				}
-
-				users = users.filter((x) => x._id !== SYSTEM_USER_ID);
-
-				let matches = (
-					search.length > 0
-						? users.filter((user) =>
-								user.username.toLowerCase().match(regex),
-						  )
-						: users
-				)
-					.splice(0, 5)
-					.filter((x) => typeof x !== "undefined");
-
-				if (matches.length > 0) {
-					let currentPosition =
-						state.type !== "none" ? state.selected : 0;
-
-					setState({
-						type: "user",
-						matches,
-						selected: Math.min(currentPosition, matches.length - 1),
-						within: false,
-					});
-
-					return;
-				}
-			}
-
-			if (type === "channel" && searchClues?.channels) {
-				let channels = client.servers
-					.get(searchClues.channels.server)
-					?.channels.map((x) => client.channels.get(x))
-					.filter(
-						(x) => typeof x !== "undefined",
-					) as Channels.TextChannel[];
-
-				let matches = (
-					search.length > 0
-						? channels.filter((channel) =>
-								channel.name.toLowerCase().match(regex),
-						  )
-						: channels
-				)
-					.splice(0, 5)
-					.filter((x) => typeof x !== "undefined");
-
-				if (matches.length > 0) {
-					let currentPosition =
-						state.type !== "none" ? state.selected : 0;
-
-					setState({
-						type: "channel",
-						matches,
-						selected: Math.min(currentPosition, matches.length - 1),
-						within: false,
-					});
-
-					return;
-				}
-			}
-		}
-
-		if (state.type !== "none") {
-			setState({ type: "none" });
-		}
-	}
-
-	function selectCurrent(el: HTMLTextAreaElement) {
-		if (state.type !== "none") {
-			let result = findSearchString(el);
-			if (result) {
-				let [_type, search, index] = result;
-
-				let content = el.value.split("");
-				if (state.type === "emoji") {
-					content.splice(
-						index,
-						search.length,
-						state.matches[state.selected],
-						": ",
-					);
-				} else if (state.type === "user") {
-					content.splice(
-						index - 1,
-						search.length + 1,
-						"<@",
-						state.matches[state.selected]._id,
-						"> ",
-					);
-				} else {
-					content.splice(
-						index - 1,
-						search.length + 1,
-						"<#",
-						state.matches[state.selected]._id,
-						"> ",
-					);
-				}
-
-				setValue(content.join(""));
-			}
-		}
-	}
-
-	function onClick(ev: JSX.TargetedMouseEvent<HTMLButtonElement>) {
-		ev.preventDefault();
-		selectCurrent(document.querySelector("#message")!);
-	}
-
-	function onKeyDown(e: KeyboardEvent) {
-		if (focused && state.type !== "none") {
-			if (e.key === "ArrowUp") {
-				e.preventDefault();
-				if (state.selected > 0) {
-					setState({
-						...state,
-						selected: state.selected - 1,
-					});
-				}
-
-				return true;
-			}
-
-			if (e.key === "ArrowDown") {
-				e.preventDefault();
-				if (state.selected < state.matches.length - 1) {
-					setState({
-						...state,
-						selected: state.selected + 1,
-					});
-				}
-
-				return true;
-			}
-
-			if (e.key === "Enter" || e.key === "Tab") {
-				e.preventDefault();
-				selectCurrent(e.currentTarget as HTMLTextAreaElement);
-
-				return true;
-			}
-		}
-
-		return false;
-	}
-
-	function onKeyUp(e: KeyboardEvent) {
-		if (e.currentTarget !== null) {
-			// @ts-expect-error
-			onChange(e);
-		}
-	}
-
-	function onFocus(ev: JSX.TargetedFocusEvent<HTMLTextAreaElement>) {
-		setFocused(true);
-		onChange(ev);
-	}
-
-	function onBlur() {
-		if (state.type !== "none" && state.within) return;
-		setFocused(false);
-	}
-
-	return {
-		state: focused ? state : { type: "none" },
-		setState,
-
-		onClick,
-		onChange,
-		onKeyUp,
-		onKeyDown,
-		onFocus,
-		onBlur,
-	};
+    const [state, setState] = useState<AutoCompleteState>({ type: "none" });
+    const [focused, setFocused] = useState(false);
+    const client = useContext(AppContext);
+
+    function findSearchString(
+        el: HTMLTextAreaElement,
+    ): ["emoji" | "user" | "channel", string, number] | undefined {
+        if (el.selectionStart === el.selectionEnd) {
+            let cursor = el.selectionStart;
+            let content = el.value.slice(0, cursor);
+
+            let valid = /\w/;
+
+            let j = content.length - 1;
+            if (content[j] === "@") {
+                return ["user", "", j];
+            } else if (content[j] === "#") {
+                return ["channel", "", j];
+            }
+
+            while (j >= 0 && valid.test(content[j])) {
+                j--;
+            }
+
+            if (j === -1) return;
+            let current = content[j];
+
+            if (current === ":" || current === "@" || current === "#") {
+                let search = content.slice(j + 1, content.length);
+                if (search.length > 0) {
+                    return [
+                        current === "#"
+                            ? "channel"
+                            : current === ":"
+                            ? "emoji"
+                            : "user",
+                        search.toLowerCase(),
+                        j + 1,
+                    ];
+                }
+            }
+        }
+    }
+
+    function onChange(ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) {
+        const el = ev.currentTarget;
+
+        let result = findSearchString(el);
+        if (result) {
+            let [type, search] = result;
+            const regex = new RegExp(search, "i");
+
+            if (type === "emoji") {
+                // ! FIXME: we should convert it to a Binary Search Tree and use that
+                let matches = Object.keys(emojiDictionary)
+                    .filter((emoji: string) => emoji.match(regex))
+                    .splice(0, 5);
+
+                if (matches.length > 0) {
+                    let currentPosition =
+                        state.type !== "none" ? state.selected : 0;
+
+                    setState({
+                        type: "emoji",
+                        matches,
+                        selected: Math.min(currentPosition, matches.length - 1),
+                        within: false,
+                    });
+
+                    return;
+                }
+            }
+
+            if (type === "user" && searchClues?.users) {
+                let users: User[] = [];
+                switch (searchClues.users.type) {
+                    case "all":
+                        users = client.users.toArray();
+                        break;
+                    case "channel": {
+                        let channel = client.channels.get(searchClues.users.id);
+                        switch (channel?.channel_type) {
+                            case "Group":
+                            case "DirectMessage":
+                                users = client.users
+                                    .mapKeys(channel.recipients)
+                                    .filter(
+                                        (x) => typeof x !== "undefined",
+                                    ) as User[];
+                                break;
+                            case "TextChannel":
+                                const server = channel.server;
+                                users = client.servers.members
+                                    .toArray()
+                                    .filter(
+                                        (x) => x._id.substr(0, 26) === server,
+                                    )
+                                    .map((x) =>
+                                        client.users.get(x._id.substr(26)),
+                                    )
+                                    .filter(
+                                        (x) => typeof x !== "undefined",
+                                    ) as User[];
+                                break;
+                            default:
+                                return;
+                        }
+                    }
+                }
+
+                users = users.filter((x) => x._id !== SYSTEM_USER_ID);
+
+                let matches = (
+                    search.length > 0
+                        ? users.filter((user) =>
+                              user.username.toLowerCase().match(regex),
+                          )
+                        : users
+                )
+                    .splice(0, 5)
+                    .filter((x) => typeof x !== "undefined");
+
+                if (matches.length > 0) {
+                    let currentPosition =
+                        state.type !== "none" ? state.selected : 0;
+
+                    setState({
+                        type: "user",
+                        matches,
+                        selected: Math.min(currentPosition, matches.length - 1),
+                        within: false,
+                    });
+
+                    return;
+                }
+            }
+
+            if (type === "channel" && searchClues?.channels) {
+                let channels = client.servers
+                    .get(searchClues.channels.server)
+                    ?.channels.map((x) => client.channels.get(x))
+                    .filter(
+                        (x) => typeof x !== "undefined",
+                    ) as Channels.TextChannel[];
+
+                let matches = (
+                    search.length > 0
+                        ? channels.filter((channel) =>
+                              channel.name.toLowerCase().match(regex),
+                          )
+                        : channels
+                )
+                    .splice(0, 5)
+                    .filter((x) => typeof x !== "undefined");
+
+                if (matches.length > 0) {
+                    let currentPosition =
+                        state.type !== "none" ? state.selected : 0;
+
+                    setState({
+                        type: "channel",
+                        matches,
+                        selected: Math.min(currentPosition, matches.length - 1),
+                        within: false,
+                    });
+
+                    return;
+                }
+            }
+        }
+
+        if (state.type !== "none") {
+            setState({ type: "none" });
+        }
+    }
+
+    function selectCurrent(el: HTMLTextAreaElement) {
+        if (state.type !== "none") {
+            let result = findSearchString(el);
+            if (result) {
+                let [_type, search, index] = result;
+
+                let content = el.value.split("");
+                if (state.type === "emoji") {
+                    content.splice(
+                        index,
+                        search.length,
+                        state.matches[state.selected],
+                        ": ",
+                    );
+                } else if (state.type === "user") {
+                    content.splice(
+                        index - 1,
+                        search.length + 1,
+                        "<@",
+                        state.matches[state.selected]._id,
+                        "> ",
+                    );
+                } else {
+                    content.splice(
+                        index - 1,
+                        search.length + 1,
+                        "<#",
+                        state.matches[state.selected]._id,
+                        "> ",
+                    );
+                }
+
+                setValue(content.join(""));
+            }
+        }
+    }
+
+    function onClick(ev: JSX.TargetedMouseEvent<HTMLButtonElement>) {
+        ev.preventDefault();
+        selectCurrent(document.querySelector("#message")!);
+    }
+
+    function onKeyDown(e: KeyboardEvent) {
+        if (focused && state.type !== "none") {
+            if (e.key === "ArrowUp") {
+                e.preventDefault();
+                if (state.selected > 0) {
+                    setState({
+                        ...state,
+                        selected: state.selected - 1,
+                    });
+                }
+
+                return true;
+            }
+
+            if (e.key === "ArrowDown") {
+                e.preventDefault();
+                if (state.selected < state.matches.length - 1) {
+                    setState({
+                        ...state,
+                        selected: state.selected + 1,
+                    });
+                }
+
+                return true;
+            }
+
+            if (e.key === "Enter" || e.key === "Tab") {
+                e.preventDefault();
+                selectCurrent(e.currentTarget as HTMLTextAreaElement);
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    function onKeyUp(e: KeyboardEvent) {
+        if (e.currentTarget !== null) {
+            // @ts-expect-error
+            onChange(e);
+        }
+    }
+
+    function onFocus(ev: JSX.TargetedFocusEvent<HTMLTextAreaElement>) {
+        setFocused(true);
+        onChange(ev);
+    }
+
+    function onBlur() {
+        if (state.type !== "none" && state.within) return;
+        setFocused(false);
+    }
+
+    return {
+        state: focused ? state : { type: "none" },
+        setState,
+
+        onClick,
+        onChange,
+        onKeyUp,
+        onKeyDown,
+        onFocus,
+        onBlur,
+    };
 }
 
 const Base = styled.div<{ detached?: boolean }>`
-	position: relative;
-
-	> div {
-		bottom: 0;
-		width: 100%;
-		position: absolute;
-		background: var(--primary-header);
-	}
-
-	button {
-		gap: 8px;
-		margin: 4px;
-		padding: 6px;
-		border: none;
-		display: flex;
-		font-size: 1em;
-		cursor: pointer;
-		border-radius: 6px;
-		align-items: center;
-		flex-direction: row;
-		background: transparent;
-		color: var(--foreground);
-		width: calc(100% - 12px);
-
-		span {
-			display: grid;
-			place-items: center;
-		}
-
-		&.active {
-			background: var(--primary-background);
-		}
-	}
-
-	${(props) =>
-		props.detached &&
-		css`
-			bottom: 8px;
-
-			> div {
-				border-radius: 4px;
-			}
-		`}
+    position: relative;
+
+    > div {
+        bottom: 0;
+        width: 100%;
+        position: absolute;
+        background: var(--primary-header);
+    }
+
+    button {
+        gap: 8px;
+        margin: 4px;
+        padding: 6px;
+        border: none;
+        display: flex;
+        font-size: 1em;
+        cursor: pointer;
+        border-radius: 6px;
+        align-items: center;
+        flex-direction: row;
+        background: transparent;
+        color: var(--foreground);
+        width: calc(100% - 12px);
+
+        span {
+            display: grid;
+            place-items: center;
+        }
+
+        &.active {
+            background: var(--primary-background);
+        }
+    }
+
+    ${(props) =>
+        props.detached &&
+        css`
+            bottom: 8px;
+
+            > div {
+                border-radius: 4px;
+            }
+        `}
 `;
 
 export default function AutoComplete({
-	detached,
-	state,
-	setState,
-	onClick,
+    detached,
+    state,
+    setState,
+    onClick,
 }: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) {
-	return (
-		<Base detached={detached}>
-			<div>
-				{state.type === "emoji" &&
-					state.matches.map((match, i) => (
-						<button
-							className={i === state.selected ? "active" : ""}
-							onMouseEnter={() =>
-								(i !== state.selected || !state.within) &&
-								setState({
-									...state,
-									selected: i,
-									within: true,
-								})
-							}
-							onMouseLeave={() =>
-								state.within &&
-								setState({
-									...state,
-									within: false,
-								})
-							}
-							onClick={onClick}>
-							<Emoji
-								emoji={
-									(emojiDictionary as Record<string, string>)[
-										match
-									]
-								}
-								size={20}
-							/>
-							:{match}:
-						</button>
-					))}
-				{state.type === "user" &&
-					state.matches.map((match, i) => (
-						<button
-							className={i === state.selected ? "active" : ""}
-							onMouseEnter={() =>
-								(i !== state.selected || !state.within) &&
-								setState({
-									...state,
-									selected: i,
-									within: true,
-								})
-							}
-							onMouseLeave={() =>
-								state.within &&
-								setState({
-									...state,
-									within: false,
-								})
-							}
-							onClick={onClick}>
-							<UserIcon size={24} target={match} status={true} />
-							{match.username}
-						</button>
-					))}
-				{state.type === "channel" &&
-					state.matches.map((match, i) => (
-						<button
-							className={i === state.selected ? "active" : ""}
-							onMouseEnter={() =>
-								(i !== state.selected || !state.within) &&
-								setState({
-									...state,
-									selected: i,
-									within: true,
-								})
-							}
-							onMouseLeave={() =>
-								state.within &&
-								setState({
-									...state,
-									within: false,
-								})
-							}
-							onClick={onClick}>
-							<ChannelIcon size={24} target={match} />
-							{match.name}
-						</button>
-					))}
-			</div>
-		</Base>
-	);
+    return (
+        <Base detached={detached}>
+            <div>
+                {state.type === "emoji" &&
+                    state.matches.map((match, i) => (
+                        <button
+                            className={i === state.selected ? "active" : ""}
+                            onMouseEnter={() =>
+                                (i !== state.selected || !state.within) &&
+                                setState({
+                                    ...state,
+                                    selected: i,
+                                    within: true,
+                                })
+                            }
+                            onMouseLeave={() =>
+                                state.within &&
+                                setState({
+                                    ...state,
+                                    within: false,
+                                })
+                            }
+                            onClick={onClick}>
+                            <Emoji
+                                emoji={
+                                    (emojiDictionary as Record<string, string>)[
+                                        match
+                                    ]
+                                }
+                                size={20}
+                            />
+                            :{match}:
+                        </button>
+                    ))}
+                {state.type === "user" &&
+                    state.matches.map((match, i) => (
+                        <button
+                            className={i === state.selected ? "active" : ""}
+                            onMouseEnter={() =>
+                                (i !== state.selected || !state.within) &&
+                                setState({
+                                    ...state,
+                                    selected: i,
+                                    within: true,
+                                })
+                            }
+                            onMouseLeave={() =>
+                                state.within &&
+                                setState({
+                                    ...state,
+                                    within: false,
+                                })
+                            }
+                            onClick={onClick}>
+                            <UserIcon size={24} target={match} status={true} />
+                            {match.username}
+                        </button>
+                    ))}
+                {state.type === "channel" &&
+                    state.matches.map((match, i) => (
+                        <button
+                            className={i === state.selected ? "active" : ""}
+                            onMouseEnter={() =>
+                                (i !== state.selected || !state.within) &&
+                                setState({
+                                    ...state,
+                                    selected: i,
+                                    within: true,
+                                })
+                            }
+                            onMouseLeave={() =>
+                                state.within &&
+                                setState({
+                                    ...state,
+                                    within: false,
+                                })
+                            }
+                            onClick={onClick}>
+                            <ChannelIcon size={24} target={match} />
+                            {match.name}
+                        </button>
+                    ))}
+            </div>
+        </Base>
+    );
 }
diff --git a/src/components/common/ChannelIcon.tsx b/src/components/common/ChannelIcon.tsx
index e3dd136..9145d5f 100644
--- a/src/components/common/ChannelIcon.tsx
+++ b/src/components/common/ChannelIcon.tsx
@@ -9,57 +9,57 @@ import { ImageIconBase, IconBaseProps } from "./IconBase";
 import fallback from "./assets/group.png";
 
 interface Props
-	extends IconBaseProps<
-		Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel
-	> {
-	isServerChannel?: boolean;
+    extends IconBaseProps<
+        Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel
+    > {
+    isServerChannel?: boolean;
 }
 
 export default function ChannelIcon(
-	props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
+    props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
 ) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	const {
-		size,
-		target,
-		attachment,
-		isServerChannel: server,
-		animate,
-		children,
-		as,
-		...imgProps
-	} = props;
-	const iconURL = client.generateFileURL(
-		target?.icon ?? attachment,
-		{ max_side: 256 },
-		animate,
-	);
-	const isServerChannel =
-		server ||
-		(target &&
-			(target.channel_type === "TextChannel" ||
-				target.channel_type === "VoiceChannel"));
+    const {
+        size,
+        target,
+        attachment,
+        isServerChannel: server,
+        animate,
+        children,
+        as,
+        ...imgProps
+    } = props;
+    const iconURL = client.generateFileURL(
+        target?.icon ?? attachment,
+        { max_side: 256 },
+        animate,
+    );
+    const isServerChannel =
+        server ||
+        (target &&
+            (target.channel_type === "TextChannel" ||
+                target.channel_type === "VoiceChannel"));
 
-	if (typeof iconURL === "undefined") {
-		if (isServerChannel) {
-			if (target?.channel_type === "VoiceChannel") {
-				return <VolumeFull size={size} />;
-			} else {
-				return <Hash size={size} />;
-			}
-		}
-	}
+    if (typeof iconURL === "undefined") {
+        if (isServerChannel) {
+            if (target?.channel_type === "VoiceChannel") {
+                return <VolumeFull size={size} />;
+            } else {
+                return <Hash size={size} />;
+            }
+        }
+    }
 
-	return (
-		// ! fixme: replace fallback with <picture /> + <source />
-		<ImageIconBase
-			{...imgProps}
-			width={size}
-			height={size}
-			aria-hidden="true"
-			square={isServerChannel}
-			src={iconURL ?? fallback}
-		/>
-	);
+    return (
+        // ! fixme: replace fallback with <picture /> + <source />
+        <ImageIconBase
+            {...imgProps}
+            width={size}
+            height={size}
+            aria-hidden="true"
+            square={isServerChannel}
+            src={iconURL ?? fallback}
+        />
+    );
 }
diff --git a/src/components/common/CollapsibleSection.tsx b/src/components/common/CollapsibleSection.tsx
index 3bd20db..ac2d980 100644
--- a/src/components/common/CollapsibleSection.tsx
+++ b/src/components/common/CollapsibleSection.tsx
@@ -8,52 +8,52 @@ import Details from "../ui/Details";
 import { Children } from "../../types/Preact";
 
 interface Props {
-	id: string;
-	defaultValue: boolean;
+    id: string;
+    defaultValue: boolean;
 
-	sticky?: boolean;
-	large?: boolean;
+    sticky?: boolean;
+    large?: boolean;
 
-	summary: Children;
-	children: Children;
+    summary: Children;
+    children: Children;
 }
 
 export default function CollapsibleSection({
-	id,
-	defaultValue,
-	summary,
-	children,
-	...detailsProps
+    id,
+    defaultValue,
+    summary,
+    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);
-		}
-	}
-
-	return (
-		<Details
-			open={state.sectionToggle[id] ?? defaultValue}
-			onToggle={(e) => setState(e.currentTarget.open)}
-			{...detailsProps}>
-			<summary>
-				<div class="padding">
-					<ChevronDown size={20} />
-					{summary}
-				</div>
-			</summary>
-			{children}
-		</Details>
-	);
+    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);
+        }
+    }
+
+    return (
+        <Details
+            open={state.sectionToggle[id] ?? defaultValue}
+            onToggle={(e) => setState(e.currentTarget.open)}
+            {...detailsProps}>
+            <summary>
+                <div class="padding">
+                    <ChevronDown size={20} />
+                    {summary}
+                </div>
+            </summary>
+            {children}
+        </Details>
+    );
 }
diff --git a/src/components/common/Emoji.tsx b/src/components/common/Emoji.tsx
index f27be2f..8117d1d 100644
--- a/src/components/common/Emoji.tsx
+++ b/src/components/common/Emoji.tsx
@@ -4,29 +4,29 @@ var EMOJI_PACK = "mutant";
 const REVISION = 3;
 
 export function setEmojiPack(pack: EmojiPacks) {
-	EMOJI_PACK = pack;
+    EMOJI_PACK = pack;
 }
 
 // Originally taken from Twemoji source code,
 // re-written by bree to be more readable.
 function codePoints(rune: string) {
-	const pairs = [];
-	let low = 0;
-	let i = 0;
+    const pairs = [];
+    let low = 0;
+    let i = 0;
 
-	while (i < rune.length) {
-		const charCode = rune.charCodeAt(i++);
-		if (low) {
-			pairs.push(0x10000 + ((low - 0xd800) << 10) + (charCode - 0xdc00));
-			low = 0;
-		} else if (0xd800 <= charCode && charCode <= 0xdbff) {
-			low = charCode;
-		} else {
-			pairs.push(charCode);
-		}
-	}
+    while (i < rune.length) {
+        const charCode = rune.charCodeAt(i++);
+        if (low) {
+            pairs.push(0x10000 + ((low - 0xd800) << 10) + (charCode - 0xdc00));
+            low = 0;
+        } else if (0xd800 <= charCode && charCode <= 0xdbff) {
+            low = charCode;
+        } else {
+            pairs.push(charCode);
+        }
+    }
 
-	return pairs;
+    return pairs;
 }
 
 // Taken from Twemoji source code.
@@ -35,38 +35,38 @@ function codePoints(rune: string) {
 const UFE0Fg = /\uFE0F/g;
 const U200D = String.fromCharCode(0x200d);
 function toCodePoint(rune: string) {
-	return codePoints(rune.indexOf(U200D) < 0 ? rune.replace(UFE0Fg, "") : rune)
-		.map((val) => val.toString(16))
-		.join("-");
+    return codePoints(rune.indexOf(U200D) < 0 ? rune.replace(UFE0Fg, "") : rune)
+        .map((val) => val.toString(16))
+        .join("-");
 }
 
 function parseEmoji(emoji: string) {
-	let codepoint = toCodePoint(emoji);
-	return `https://static.revolt.chat/emoji/${EMOJI_PACK}/${codepoint}.svg?rev=${REVISION}`;
+    let codepoint = toCodePoint(emoji);
+    return `https://static.revolt.chat/emoji/${EMOJI_PACK}/${codepoint}.svg?rev=${REVISION}`;
 }
 
 export default function Emoji({
-	emoji,
-	size,
+    emoji,
+    size,
 }: {
-	emoji: string;
-	size?: number;
+    emoji: string;
+    size?: number;
 }) {
-	return (
-		<img
-			alt={emoji}
-			className="emoji"
-			draggable={false}
-			src={parseEmoji(emoji)}
-			style={
-				size ? { width: `${size}px`, height: `${size}px` } : undefined
-			}
-		/>
-	);
+    return (
+        <img
+            alt={emoji}
+            className="emoji"
+            draggable={false}
+            src={parseEmoji(emoji)}
+            style={
+                size ? { width: `${size}px`, height: `${size}px` } : undefined
+            }
+        />
+    );
 }
 
 export function generateEmoji(emoji: string) {
-	return `<img class="emoji" draggable="false" alt="${emoji}" src="${parseEmoji(
-		emoji,
-	)}" />`;
+    return `<img class="emoji" draggable="false" alt="${emoji}" src="${parseEmoji(
+        emoji,
+    )}" />`;
 }
diff --git a/src/components/common/IconBase.tsx b/src/components/common/IconBase.tsx
index 56640d9..305e5a3 100644
--- a/src/components/common/IconBase.tsx
+++ b/src/components/common/IconBase.tsx
@@ -2,40 +2,40 @@ import { Attachment } from "revolt.js/dist/api/objects";
 import styled, { css } from "styled-components";
 
 export interface IconBaseProps<T> {
-	target?: T;
-	attachment?: Attachment;
+    target?: T;
+    attachment?: Attachment;
 
-	size: number;
-	animate?: boolean;
+    size: number;
+    animate?: boolean;
 }
 
 interface IconModifiers {
-	square?: boolean;
+    square?: boolean;
 }
 
 export default styled.svg<IconModifiers>`
-	flex-shrink: 0;
-
-	img {
-		width: 100%;
-		height: 100%;
-		object-fit: cover;
-
-		${(props) =>
-			!props.square &&
-			css`
-				border-radius: 50%;
-			`}
-	}
+    flex-shrink: 0;
+
+    img {
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+
+        ${(props) =>
+            !props.square &&
+            css`
+                border-radius: 50%;
+            `}
+    }
 `;
 
 export const ImageIconBase = styled.img<IconModifiers>`
-	flex-shrink: 0;
-	object-fit: cover;
-
-	${(props) =>
-		!props.square &&
-		css`
-			border-radius: 50%;
-		`}
+    flex-shrink: 0;
+    object-fit: cover;
+
+    ${(props) =>
+        !props.square &&
+        css`
+            border-radius: 50%;
+        `}
 `;
diff --git a/src/components/common/LocaleSelector.tsx b/src/components/common/LocaleSelector.tsx
index c214e6b..31c0de0 100644
--- a/src/components/common/LocaleSelector.tsx
+++ b/src/components/common/LocaleSelector.tsx
@@ -6,33 +6,33 @@ import { Language, Languages } from "../../context/Locale";
 import ComboBox from "../ui/ComboBox";
 
 type Props = {
-	locale: string;
+    locale: string;
 };
 
 export function LocaleSelector(props: Props) {
-	return (
-		<ComboBox
-			value={props.locale}
-			onChange={(e) =>
-				dispatch({
-					type: "SET_LOCALE",
-					locale: e.currentTarget.value as Language,
-				})
-			}>
-			{Object.keys(Languages).map((x) => {
-				const l = Languages[x as keyof typeof Languages];
-				return (
-					<option value={x}>
-						{l.emoji} {l.display}
-					</option>
-				);
-			})}
-		</ComboBox>
-	);
+    return (
+        <ComboBox
+            value={props.locale}
+            onChange={(e) =>
+                dispatch({
+                    type: "SET_LOCALE",
+                    locale: e.currentTarget.value as Language,
+                })
+            }>
+            {Object.keys(Languages).map((x) => {
+                const l = Languages[x as keyof typeof Languages];
+                return (
+                    <option value={x}>
+                        {l.emoji} {l.display}
+                    </option>
+                );
+            })}
+        </ComboBox>
+    );
 }
 
 export default connectState(LocaleSelector, (state) => {
-	return {
-		locale: state.locale,
-	};
+    return {
+        locale: state.locale,
+    };
 });
diff --git a/src/components/common/ServerHeader.tsx b/src/components/common/ServerHeader.tsx
index 46e6cd5..91cc0b3 100644
--- a/src/components/common/ServerHeader.tsx
+++ b/src/components/common/ServerHeader.tsx
@@ -10,40 +10,40 @@ import Header from "../ui/Header";
 import IconButton from "../ui/IconButton";
 
 interface Props {
-	server: Server;
-	ctx: HookContext;
+    server: Server;
+    ctx: HookContext;
 }
 
 const ServerName = styled.div`
-	flex-grow: 1;
+    flex-grow: 1;
 `;
 
 export default function ServerHeader({ server, ctx }: Props) {
-	const permissions = useServerPermission(server._id, ctx);
-	const bannerURL = ctx.client.servers.getBannerURL(
-		server._id,
-		{ width: 480 },
-		true,
-	);
+    const permissions = useServerPermission(server._id, ctx);
+    const bannerURL = ctx.client.servers.getBannerURL(
+        server._id,
+        { width: 480 },
+        true,
+    );
 
-	return (
-		<Header
-			borders
-			placement="secondary"
-			background={typeof bannerURL !== "undefined"}
-			style={{
-				background: bannerURL ? `url('${bannerURL}')` : undefined,
-			}}>
-			<ServerName>{server.name}</ServerName>
-			{(permissions & ServerPermission.ManageServer) > 0 && (
-				<div className="actions">
-					<Link to={`/server/${server._id}/settings`}>
-						<IconButton>
-							<Cog size={24} />
-						</IconButton>
-					</Link>
-				</div>
-			)}
-		</Header>
-	);
+    return (
+        <Header
+            borders
+            placement="secondary"
+            background={typeof bannerURL !== "undefined"}
+            style={{
+                background: bannerURL ? `url('${bannerURL}')` : undefined,
+            }}>
+            <ServerName>{server.name}</ServerName>
+            {(permissions & ServerPermission.ManageServer) > 0 && (
+                <div className="actions">
+                    <Link to={`/server/${server._id}/settings`}>
+                        <IconButton>
+                            <Cog size={24} />
+                        </IconButton>
+                    </Link>
+                </div>
+            )}
+        </Header>
+    );
 }
diff --git a/src/components/common/ServerIcon.tsx b/src/components/common/ServerIcon.tsx
index 061a18d..5b9dad8 100644
--- a/src/components/common/ServerIcon.tsx
+++ b/src/components/common/ServerIcon.tsx
@@ -8,61 +8,61 @@ import { AppContext } from "../../context/revoltjs/RevoltClient";
 import { IconBaseProps, ImageIconBase } from "./IconBase";
 
 interface Props extends IconBaseProps<Server> {
-	server_name?: string;
+    server_name?: string;
 }
 
 const ServerText = styled.div`
-	display: grid;
-	padding: 0.2em;
-	overflow: hidden;
-	border-radius: 50%;
-	place-items: center;
-	color: var(--foreground);
-	background: var(--primary-background);
+    display: grid;
+    padding: 0.2em;
+    overflow: hidden;
+    border-radius: 50%;
+    place-items: center;
+    color: var(--foreground);
+    background: var(--primary-background);
 `;
 
 const fallback = "/assets/group.png";
 export default function ServerIcon(
-	props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
+    props: Props & Omit<JSX.HTMLAttributes<HTMLImageElement>, keyof Props>,
 ) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	const {
-		target,
-		attachment,
-		size,
-		animate,
-		server_name,
-		children,
-		as,
-		...imgProps
-	} = props;
-	const iconURL = client.generateFileURL(
-		target?.icon ?? attachment,
-		{ max_side: 256 },
-		animate,
-	);
+    const {
+        target,
+        attachment,
+        size,
+        animate,
+        server_name,
+        children,
+        as,
+        ...imgProps
+    } = props;
+    const iconURL = client.generateFileURL(
+        target?.icon ?? attachment,
+        { max_side: 256 },
+        animate,
+    );
 
-	if (typeof iconURL === "undefined") {
-		const name = target?.name ?? server_name ?? "";
+    if (typeof iconURL === "undefined") {
+        const name = target?.name ?? server_name ?? "";
 
-		return (
-			<ServerText style={{ width: size, height: size }}>
-				{name
-					.split(" ")
-					.map((x) => x[0])
-					.filter((x) => typeof x !== "undefined")}
-			</ServerText>
-		);
-	}
+        return (
+            <ServerText style={{ width: size, height: size }}>
+                {name
+                    .split(" ")
+                    .map((x) => x[0])
+                    .filter((x) => typeof x !== "undefined")}
+            </ServerText>
+        );
+    }
 
-	return (
-		<ImageIconBase
-			{...imgProps}
-			width={size}
-			height={size}
-			aria-hidden="true"
-			src={iconURL}
-		/>
-	);
+    return (
+        <ImageIconBase
+            {...imgProps}
+            width={size}
+            height={size}
+            aria-hidden="true"
+            src={iconURL}
+        />
+    );
 }
diff --git a/src/components/common/Tooltip.tsx b/src/components/common/Tooltip.tsx
index 2dbfa24..1622532 100644
--- a/src/components/common/Tooltip.tsx
+++ b/src/components/common/Tooltip.tsx
@@ -6,55 +6,55 @@ import { Text } from "preact-i18n";
 import { Children } from "../../types/Preact";
 
 type Props = Omit<TippyProps, "children"> & {
-	children: Children;
-	content: Children;
+    children: Children;
+    content: Children;
 };
 
 export default function Tooltip(props: Props) {
-	const { children, content, ...tippyProps } = props;
+    const { children, content, ...tippyProps } = props;
 
-	return (
-		<Tippy content={content} {...tippyProps}>
-			{/*
+    return (
+        <Tippy content={content} {...tippyProps}>
+            {/*
             // @ts-expect-error */}
-			<div>{children}</div>
-		</Tippy>
-	);
+            <div>{children}</div>
+        </Tippy>
+    );
 }
 
 const PermissionTooltipBase = styled.div`
-	display: flex;
-	align-items: center;
-	flex-direction: column;
-
-	span {
-		font-weight: 700;
-		text-transform: uppercase;
-		color: var(--secondary-foreground);
-		font-size: 11px;
-	}
-
-	code {
-		font-family: var(--monoscape-font);
-	}
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+
+    span {
+        font-weight: 700;
+        text-transform: uppercase;
+        color: var(--secondary-foreground);
+        font-size: 11px;
+    }
+
+    code {
+        font-family: var(--monoscape-font);
+    }
 `;
 
 export function PermissionTooltip(
-	props: Omit<Props, "content"> & { permission: string },
+    props: Omit<Props, "content"> & { permission: string },
 ) {
-	const { permission, ...tooltipProps } = props;
-
-	return (
-		<Tooltip
-			content={
-				<PermissionTooltipBase>
-					<span>
-						<Text id="app.permissions.required" />
-					</span>
-					<code>{permission}</code>
-				</PermissionTooltipBase>
-			}
-			{...tooltipProps}
-		/>
-	);
+    const { permission, ...tooltipProps } = props;
+
+    return (
+        <Tooltip
+            content={
+                <PermissionTooltipBase>
+                    <span>
+                        <Text id="app.permissions.required" />
+                    </span>
+                    <code>{permission}</code>
+                </PermissionTooltipBase>
+            }
+            {...tooltipProps}
+        />
+    );
 }
diff --git a/src/components/common/UpdateIndicator.tsx b/src/components/common/UpdateIndicator.tsx
index d818c2e..e7c8cd9 100644
--- a/src/components/common/UpdateIndicator.tsx
+++ b/src/components/common/UpdateIndicator.tsx
@@ -14,18 +14,18 @@ var pendingUpdate = false;
 internalSubscribe("PWA", "update", () => (pendingUpdate = true));
 
 export default function UpdateIndicator() {
-	const [pending, setPending] = useState(pendingUpdate);
+    const [pending, setPending] = useState(pendingUpdate);
 
-	useEffect(() => {
-		return internalSubscribe("PWA", "update", () => setPending(true));
-	});
+    useEffect(() => {
+        return internalSubscribe("PWA", "update", () => setPending(true));
+    });
 
-	if (!pending) return null;
-	const theme = useContext(ThemeContext);
+    if (!pending) return null;
+    const theme = useContext(ThemeContext);
 
-	return (
-		<IconButton onClick={() => updateSW(true)}>
-			<Download size={22} color={theme.success} />
-		</IconButton>
-	);
+    return (
+        <IconButton onClick={() => updateSW(true)}>
+            <Download size={22} color={theme.success} />
+        </IconButton>
+    );
 }
diff --git a/src/components/common/messaging/Message.tsx b/src/components/common/messaging/Message.tsx
index 1dda28d..f2ca22c 100644
--- a/src/components/common/messaging/Message.tsx
+++ b/src/components/common/messaging/Message.tsx
@@ -16,118 +16,118 @@ import Markdown from "../../markdown/Markdown";
 import UserIcon from "../user/UserIcon";
 import { Username } from "../user/UserShort";
 import MessageBase, {
-	MessageContent,
-	MessageDetail,
-	MessageInfo,
+    MessageContent,
+    MessageDetail,
+    MessageInfo,
 } from "./MessageBase";
 import Attachment from "./attachments/Attachment";
 import { MessageReply } from "./attachments/MessageReply";
 import Embed from "./embed/Embed";
 
 interface Props {
-	attachContext?: boolean;
-	queued?: QueuedMessage;
-	message: MessageObject;
-	contrast?: boolean;
-	content?: Children;
-	head?: boolean;
+    attachContext?: boolean;
+    queued?: QueuedMessage;
+    message: MessageObject;
+    contrast?: boolean;
+    content?: Children;
+    head?: boolean;
 }
 
 function Message({
-	attachContext,
-	message,
-	contrast,
-	content: replacement,
-	head: preferHead,
-	queued,
+    attachContext,
+    message,
+    contrast,
+    content: replacement,
+    head: preferHead,
+    queued,
 }: Props) {
-	// TODO: Can improve re-renders here by providing a list
-	// TODO: of dependencies. We only need to update on u/avatar.
-	const user = useUser(message.author);
-	const client = useContext(AppContext);
-	const { openScreen } = useIntermediate();
+    // TODO: Can improve re-renders here by providing a list
+    // TODO: of dependencies. We only need to update on u/avatar.
+    const user = useUser(message.author);
+    const client = useContext(AppContext);
+    const { openScreen } = useIntermediate();
 
-	const content = message.content as string;
-	const head = preferHead || (message.replies && message.replies.length > 0);
+    const content = message.content as string;
+    const head = preferHead || (message.replies && message.replies.length > 0);
 
-	// ! FIXME: tell fatal to make this type generic
-	// bree: Fatal please...
-	const userContext = attachContext
-		? (attachContextMenu("Menu", {
-				user: message.author,
-				contextualChannel: message.channel,
-		  }) as any)
-		: undefined;
+    // ! FIXME: tell fatal to make this type generic
+    // bree: Fatal please...
+    const userContext = attachContext
+        ? (attachContextMenu("Menu", {
+              user: message.author,
+              contextualChannel: message.channel,
+          }) as any)
+        : undefined;
 
-	const openProfile = () =>
-		openScreen({ id: "profile", user_id: message.author });
+    const openProfile = () =>
+        openScreen({ id: "profile", user_id: message.author });
 
-	return (
-		<div id={message._id}>
-			{message.replies?.map((message_id, index) => (
-				<MessageReply
-					index={index}
-					id={message_id}
-					channel={message.channel}
-				/>
-			))}
-			<MessageBase
-				head={head && !(message.replies && message.replies.length > 0)}
-				contrast={contrast}
-				sending={typeof queued !== "undefined"}
-				mention={message.mentions?.includes(client.user!._id)}
-				failed={typeof queued?.error !== "undefined"}
-				onContextMenu={
-					attachContext
-						? attachContextMenu("Menu", {
-								message,
-								contextualChannel: message.channel,
-								queued,
-						  })
-						: undefined
-				}>
-				<MessageInfo>
-					{head ? (
-						<UserIcon
-							target={user}
-							size={36}
-							onContextMenu={userContext}
-							onClick={openProfile}
-						/>
-					) : (
-						<MessageDetail message={message} position="left" />
-					)}
-				</MessageInfo>
-				<MessageContent>
-					{head && (
-						<span className="detail">
-							<Username
-								className="author"
-								user={user}
-								onContextMenu={userContext}
-								onClick={openProfile}
-							/>
-							<MessageDetail message={message} position="top" />
-						</span>
-					)}
-					{replacement ?? <Markdown content={content} />}
-					{queued?.error && (
-						<Overline type="error" error={queued.error} />
-					)}
-					{message.attachments?.map((attachment, index) => (
-						<Attachment
-							key={index}
-							attachment={attachment}
-							hasContent={index > 0 || content.length > 0}
-						/>
-					))}
-					{message.embeds?.map((embed, index) => (
-						<Embed key={index} embed={embed} />
-					))}
-				</MessageContent>
-			</MessageBase>
-		</div>
-	);
+    return (
+        <div id={message._id}>
+            {message.replies?.map((message_id, index) => (
+                <MessageReply
+                    index={index}
+                    id={message_id}
+                    channel={message.channel}
+                />
+            ))}
+            <MessageBase
+                head={head && !(message.replies && message.replies.length > 0)}
+                contrast={contrast}
+                sending={typeof queued !== "undefined"}
+                mention={message.mentions?.includes(client.user!._id)}
+                failed={typeof queued?.error !== "undefined"}
+                onContextMenu={
+                    attachContext
+                        ? attachContextMenu("Menu", {
+                              message,
+                              contextualChannel: message.channel,
+                              queued,
+                          })
+                        : undefined
+                }>
+                <MessageInfo>
+                    {head ? (
+                        <UserIcon
+                            target={user}
+                            size={36}
+                            onContextMenu={userContext}
+                            onClick={openProfile}
+                        />
+                    ) : (
+                        <MessageDetail message={message} position="left" />
+                    )}
+                </MessageInfo>
+                <MessageContent>
+                    {head && (
+                        <span className="detail">
+                            <Username
+                                className="author"
+                                user={user}
+                                onContextMenu={userContext}
+                                onClick={openProfile}
+                            />
+                            <MessageDetail message={message} position="top" />
+                        </span>
+                    )}
+                    {replacement ?? <Markdown content={content} />}
+                    {queued?.error && (
+                        <Overline type="error" error={queued.error} />
+                    )}
+                    {message.attachments?.map((attachment, index) => (
+                        <Attachment
+                            key={index}
+                            attachment={attachment}
+                            hasContent={index > 0 || content.length > 0}
+                        />
+                    ))}
+                    {message.embeds?.map((embed, index) => (
+                        <Embed key={index} embed={embed} />
+                    ))}
+                </MessageContent>
+            </MessageBase>
+        </div>
+    );
 }
 
 export default memo(Message);
diff --git a/src/components/common/messaging/MessageBase.tsx b/src/components/common/messaging/MessageBase.tsx
index ee9fa94..afddb0d 100644
--- a/src/components/common/messaging/MessageBase.tsx
+++ b/src/components/common/messaging/MessageBase.tsx
@@ -9,204 +9,204 @@ import { MessageObject } from "../../../context/revoltjs/util";
 import Tooltip from "../Tooltip";
 
 export interface BaseMessageProps {
-	head?: boolean;
-	failed?: boolean;
-	mention?: boolean;
-	blocked?: boolean;
-	sending?: boolean;
-	contrast?: boolean;
+    head?: boolean;
+    failed?: boolean;
+    mention?: boolean;
+    blocked?: boolean;
+    sending?: boolean;
+    contrast?: boolean;
 }
 
 export default styled.div<BaseMessageProps>`
-	display: flex;
-	overflow-x: none;
-	padding: 0.125rem;
-	flex-direction: row;
-	padding-right: 16px;
-
-	${(props) =>
-		props.contrast &&
-		css`
-			padding: 0.3rem;
-			border-radius: 4px;
-			background: var(--hover);
-		`}
-
-	${(props) =>
-		props.head &&
-		css`
-			margin-top: 12px;
-		`}
+    display: flex;
+    overflow-x: none;
+    padding: 0.125rem;
+    flex-direction: row;
+    padding-right: 16px;
 
     ${(props) =>
-		props.mention &&
-		css`
-			background: var(--mention);
-		`}
+        props.contrast &&
+        css`
+            padding: 0.3rem;
+            border-radius: 4px;
+            background: var(--hover);
+        `}
 
     ${(props) =>
-		props.blocked &&
-		css`
-			filter: blur(4px);
-			transition: 0.2s ease filter;
+        props.head &&
+        css`
+            margin-top: 12px;
+        `}
 
-			&:hover {
-				filter: none;
-			}
-		`}
+    ${(props) =>
+        props.mention &&
+        css`
+            background: var(--mention);
+        `}
+
+    ${(props) =>
+        props.blocked &&
+        css`
+            filter: blur(4px);
+            transition: 0.2s ease filter;
+
+            &:hover {
+                filter: none;
+            }
+        `}
 
     ${(props) =>
-		props.sending &&
-		css`
-			opacity: 0.8;
-			color: var(--tertiary-foreground);
-		`}
+        props.sending &&
+        css`
+            opacity: 0.8;
+            color: var(--tertiary-foreground);
+        `}
 
     ${(props) =>
-		props.failed &&
-		css`
-			color: var(--error);
-		`}
+        props.failed &&
+        css`
+            color: var(--error);
+        `}
 
     .detail {
-		gap: 8px;
-		display: flex;
-		align-items: center;
-	}
-
-	.author {
-		cursor: pointer;
-		font-weight: 600 !important;
-
-		&:hover {
-			text-decoration: underline;
-		}
-	}
-
-	.copy {
-		display: block;
-		overflow: hidden;
-	}
-
-	&:hover {
-		background: var(--hover);
-
-		time {
-			opacity: 1;
-		}
-	}
+        gap: 8px;
+        display: flex;
+        align-items: center;
+    }
+
+    .author {
+        cursor: pointer;
+        font-weight: 600 !important;
+
+        &:hover {
+            text-decoration: underline;
+        }
+    }
+
+    .copy {
+        display: block;
+        overflow: hidden;
+    }
+
+    &:hover {
+        background: var(--hover);
+
+        time {
+            opacity: 1;
+        }
+    }
 `;
 
 export const MessageInfo = styled.div`
-	width: 62px;
-	display: flex;
-	flex-shrink: 0;
-	padding-top: 2px;
-	flex-direction: row;
-	justify-content: center;
-
-	.copyBracket {
-		opacity: 0;
-		position: absolute;
-	}
-
-	.copyTime {
-		opacity: 0;
-		position: absolute;
-	}
-
-	svg {
-		user-select: none;
-		cursor: pointer;
-
-		&:active {
-			transform: translateY(1px);
-		}
-	}
-
-	time {
-		opacity: 0;
-	}
-
-	time,
-	.edited {
-		margin-top: 1px;
-		cursor: default;
-		display: inline;
-		font-size: 10px;
-		color: var(--tertiary-foreground);
-	}
-
-	time,
-	.edited > div {
-		&::selection {
-			background-color: transparent;
-			color: var(--tertiary-foreground);
-		}
-	}
+    width: 62px;
+    display: flex;
+    flex-shrink: 0;
+    padding-top: 2px;
+    flex-direction: row;
+    justify-content: center;
+
+    .copyBracket {
+        opacity: 0;
+        position: absolute;
+    }
+
+    .copyTime {
+        opacity: 0;
+        position: absolute;
+    }
+
+    svg {
+        user-select: none;
+        cursor: pointer;
+
+        &:active {
+            transform: translateY(1px);
+        }
+    }
+
+    time {
+        opacity: 0;
+    }
+
+    time,
+    .edited {
+        margin-top: 1px;
+        cursor: default;
+        display: inline;
+        font-size: 10px;
+        color: var(--tertiary-foreground);
+    }
+
+    time,
+    .edited > div {
+        &::selection {
+            background-color: transparent;
+            color: var(--tertiary-foreground);
+        }
+    }
 `;
 
 export const MessageContent = styled.div`
-	min-width: 0;
-	flex-grow: 1;
-	display: flex;
-	// overflow: hidden;
-	font-size: 0.875rem;
-	flex-direction: column;
-	justify-content: center;
+    min-width: 0;
+    flex-grow: 1;
+    display: flex;
+    // overflow: hidden;
+    font-size: 0.875rem;
+    flex-direction: column;
+    justify-content: center;
 `;
 
 export const DetailBase = styled.div`
-	gap: 4px;
-	font-size: 10px;
-	display: inline-flex;
-	color: var(--tertiary-foreground);
+    gap: 4px;
+    font-size: 10px;
+    display: inline-flex;
+    color: var(--tertiary-foreground);
 `;
 
 export function MessageDetail({
-	message,
-	position,
+    message,
+    position,
 }: {
-	message: MessageObject;
-	position: "left" | "top";
+    message: MessageObject;
+    position: "left" | "top";
 }) {
-	if (position === "left") {
-		if (message.edited) {
-			return (
-				<>
-					<time className="copyTime">
-						<i className="copyBracket">[</i>
-						{dayjs(decodeTime(message._id)).format("H:mm")}
-						<i className="copyBracket">]</i>
-					</time>
-					<span className="edited">
-						<Tooltip content={dayjs(message.edited).format("LLLL")}>
-							<Text id="app.main.channel.edited" />
-						</Tooltip>
-					</span>
-				</>
-			);
-		} else {
-			return (
-				<>
-					<time>
-						<i className="copyBracket">[</i>
-						{dayjs(decodeTime(message._id)).format("H:mm")}
-						<i className="copyBracket">]</i>
-					</time>
-				</>
-			);
-		}
-	}
-
-	return (
-		<DetailBase>
-			<time>{dayjs(decodeTime(message._id)).calendar()}</time>
-			{message.edited && (
-				<Tooltip content={dayjs(message.edited).format("LLLL")}>
-					<Text id="app.main.channel.edited" />
-				</Tooltip>
-			)}
-		</DetailBase>
-	);
+    if (position === "left") {
+        if (message.edited) {
+            return (
+                <>
+                    <time className="copyTime">
+                        <i className="copyBracket">[</i>
+                        {dayjs(decodeTime(message._id)).format("H:mm")}
+                        <i className="copyBracket">]</i>
+                    </time>
+                    <span className="edited">
+                        <Tooltip content={dayjs(message.edited).format("LLLL")}>
+                            <Text id="app.main.channel.edited" />
+                        </Tooltip>
+                    </span>
+                </>
+            );
+        } else {
+            return (
+                <>
+                    <time>
+                        <i className="copyBracket">[</i>
+                        {dayjs(decodeTime(message._id)).format("H:mm")}
+                        <i className="copyBracket">]</i>
+                    </time>
+                </>
+            );
+        }
+    }
+
+    return (
+        <DetailBase>
+            <time>{dayjs(decodeTime(message._id)).calendar()}</time>
+            {message.edited && (
+                <Tooltip content={dayjs(message.edited).format("LLLL")}>
+                    <Text id="app.main.channel.edited" />
+                </Tooltip>
+            )}
+        </DetailBase>
+    );
 }
diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx
index a1e78ad..fe323b1 100644
--- a/src/components/common/messaging/MessageBox.tsx
+++ b/src/components/common/messaging/MessageBox.tsx
@@ -16,8 +16,8 @@ import { internalEmit, internalSubscribe } from "../../../lib/eventEmitter";
 import { useTranslation } from "../../../lib/i18n";
 import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
 import {
-	SingletonMessageRenderer,
-	SMOOTH_SCROLL_ON_RECEIVE,
+    SingletonMessageRenderer,
+    SMOOTH_SCROLL_ON_RECEIVE,
 } from "../../../lib/renderer/Singleton";
 
 import { dispatch } from "../../../redux";
@@ -27,9 +27,9 @@ import { Reply } from "../../../redux/reducers/queue";
 import { SoundContext } from "../../../context/Settings";
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import {
-	FileUploader,
-	grabFiles,
-	uploadFile,
+    FileUploader,
+    grabFiles,
+    uploadFile,
 } from "../../../context/revoltjs/FileUploads";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { useChannelPermission } from "../../../context/revoltjs/hooks";
@@ -43,454 +43,452 @@ import FilePreview from "./bars/FilePreview";
 import ReplyBar from "./bars/ReplyBar";
 
 type Props = {
-	channel: Channel;
-	draft?: string;
+    channel: Channel;
+    draft?: string;
 };
 
 export type UploadState =
-	| { type: "none" }
-	| { type: "attached"; files: File[] }
-	| {
-			type: "uploading";
-			files: File[];
-			percent: number;
-			cancel: CancelTokenSource;
-	  }
-	| { type: "sending"; files: File[] }
-	| { type: "failed"; files: File[]; error: string };
+    | { type: "none" }
+    | { type: "attached"; files: File[] }
+    | {
+          type: "uploading";
+          files: File[];
+          percent: number;
+          cancel: CancelTokenSource;
+      }
+    | { type: "sending"; files: File[] }
+    | { type: "failed"; files: File[]; error: string };
 
 const Base = styled.div`
-	display: flex;
-	padding: 0 12px;
-	background: var(--message-box);
-
-	textarea {
-		font-size: 0.875rem;
-		background: transparent;
-	}
+    display: flex;
+    padding: 0 12px;
+    background: var(--message-box);
+
+    textarea {
+        font-size: 0.875rem;
+        background: transparent;
+    }
 `;
 
 const Blocked = styled.div`
-	display: flex;
-	align-items: center;
-	padding: 14px 0;
-	user-select: none;
-	font-size: 0.875rem;
-	color: var(--tertiary-foreground);
-
-	svg {
-		flex-shrink: 0;
-		margin-inline-end: 10px;
-	}
+    display: flex;
+    align-items: center;
+    padding: 14px 0;
+    user-select: none;
+    font-size: 0.875rem;
+    color: var(--tertiary-foreground);
+
+    svg {
+        flex-shrink: 0;
+        margin-inline-end: 10px;
+    }
 `;
 
 const Action = styled.div`
-	display: grid;
-	place-items: center;
+    display: grid;
+    place-items: center;
 `;
 
 // ! FIXME: add to app config and load from app config
 export const CAN_UPLOAD_AT_ONCE = 5;
 
 function MessageBox({ channel, draft }: Props) {
-	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();
-
-	const permissions = useChannelPermission(channel._id);
-	if (!(permissions & ChannelPermission.SendMessage)) {
-		return (
-			<Base>
-				<Blocked>
-					<PermissionTooltip
-						permission="SendMessages"
-						placement="top">
-						<ShieldX size={22} />
-					</PermissionTooltip>
-					<Text id="app.main.channel.misc.no_sending" />
-				</Blocked>
-			</Base>
-		);
-	}
-
-	function setMessage(content?: string) {
-		if (content) {
-			dispatch({
-				type: "SET_DRAFT",
-				channel: channel._id,
-				content,
-			});
-		} else {
-			dispatch({
-				type: "CLEAR_DRAFT",
-				channel: channel._id,
-			});
-		}
-	}
-
-	useEffect(() => {
-		function append(content: string, action: "quote" | "mention") {
-			const text =
-				action === "quote"
-					? `${content
-							.split("\n")
-							.map((x) => `> ${x}`)
-							.join("\n")}\n\n`
-					: `${content} `;
-
-			if (!draft || draft.length === 0) {
-				setMessage(text);
-			} else {
-				setMessage(`${draft}\n${text}`);
-			}
-		}
-
-		return internalSubscribe("MessageBox", "append", append);
-	}, [draft]);
-
-	async function send() {
-		if (uploadState.type === "uploading" || uploadState.type === "sending")
-			return;
-
-		const content = draft?.trim() ?? "";
-		if (uploadState.type === "attached") return sendFile(content);
-		if (content.length === 0) return;
-
-		stopTyping();
-		setMessage();
-		setReplies([]);
-		playSound("outbound");
-
-		const nonce = ulid();
-		dispatch({
-			type: "QUEUE_ADD",
-			nonce,
-			channel: channel._id,
-			message: {
-				_id: nonce,
-				channel: channel._id,
-				author: client.user!._id,
-
-				content,
-				replies,
-			},
-		});
-
-		defer(() =>
-			SingletonMessageRenderer.jumpToBottom(
-				channel._id,
-				SMOOTH_SCROLL_ON_RECEIVE,
-			),
-		);
-
-		try {
-			await client.channels.sendMessage(channel._id, {
-				content,
-				nonce,
-				replies,
-			});
-		} catch (error) {
-			dispatch({
-				type: "QUEUE_FAIL",
-				error: takeError(error),
-				nonce,
-			});
-		}
-	}
-
-	async function sendFile(content: string) {
-		if (uploadState.type !== "attached") return;
-		let attachments: string[] = [];
-
-		const cancel = Axios.CancelToken.source();
-		const files = uploadState.files;
-		stopTyping();
-		setUploadState({ type: "uploading", files, percent: 0, cancel });
-
-		try {
-			for (let i = 0; i < files.length && i < CAN_UPLOAD_AT_ONCE; i++) {
-				const file = files[i];
-				attachments.push(
-					await uploadFile(
-						client.configuration!.features.autumn.url,
-						"attachments",
-						file,
-						{
-							onUploadProgress: (e) =>
-								setUploadState({
-									type: "uploading",
-									files,
-									percent: Math.round(
-										(i * 100 + (100 * e.loaded) / e.total) /
-											Math.min(
-												files.length,
-												CAN_UPLOAD_AT_ONCE,
-											),
-									),
-									cancel,
-								}),
-							cancelToken: cancel.token,
-						},
-					),
-				);
-			}
-		} catch (err) {
-			if (err?.message === "cancel") {
-				setUploadState({
-					type: "attached",
-					files,
-				});
-			} else {
-				setUploadState({
-					type: "failed",
-					files,
-					error: takeError(err),
-				});
-			}
-
-			return;
-		}
-
-		setUploadState({
-			type: "sending",
-			files,
-		});
-
-		const nonce = ulid();
-		try {
-			await client.channels.sendMessage(channel._id, {
-				content,
-				nonce,
-				replies,
-				attachments,
-			});
-		} catch (err) {
-			setUploadState({
-				type: "failed",
-				files,
-				error: takeError(err),
-			});
-
-			return;
-		}
-
-		setMessage();
-		setReplies([]);
-		playSound("outbound");
-
-		if (files.length > CAN_UPLOAD_AT_ONCE) {
-			setUploadState({
-				type: "attached",
-				files: files.slice(CAN_UPLOAD_AT_ONCE),
-			});
-		} else {
-			setUploadState({ type: "none" });
-		}
-	}
-
-	function startTyping() {
-		if (typeof typing === "number" && +new Date() < typing) return;
-
-		const ws = client.websocket;
-		if (ws.connected) {
-			setTyping(+new Date() + 4000);
-			ws.send({
-				type: "BeginTyping",
-				channel: channel._id,
-			});
-		}
-	}
-
-	function stopTyping(force?: boolean) {
-		if (force || typing) {
-			const ws = client.websocket;
-			if (ws.connected) {
-				setTyping(false);
-				ws.send({
-					type: "EndTyping",
-					channel: channel._id,
-				});
-			}
-		}
-	}
-
-	const debouncedStopTyping = useCallback(debounce(stopTyping, 1000), [
-		channel._id,
-	]);
-	const {
-		onChange,
-		onKeyUp,
-		onKeyDown,
-		onFocus,
-		onBlur,
-		...autoCompleteProps
-	} = useAutoComplete(setMessage, {
-		users: { type: "channel", id: channel._id },
-		channels:
-			channel.channel_type === "TextChannel"
-				? { server: channel.server }
-				: undefined,
-	});
-
-	return (
-		<>
-			<AutoComplete {...autoCompleteProps} />
-			<FilePreview
-				state={uploadState}
-				addFile={() =>
-					uploadState.type === "attached" &&
-					grabFiles(
-						20_000_000,
-						(files) =>
-							setUploadState({
-								type: "attached",
-								files: [...uploadState.files, ...files],
-							}),
-						() =>
-							openScreen({ id: "error", error: "FileTooLarge" }),
-						true,
-					)
-				}
-				removeFile={(index) => {
-					if (uploadState.type !== "attached") return;
-					if (uploadState.files.length === 1) {
-						setUploadState({ type: "none" });
-					} else {
-						setUploadState({
-							type: "attached",
-							files: uploadState.files.filter(
-								(_, i) => index !== i,
-							),
-						});
-					}
-				}}
-			/>
-			<ReplyBar
-				channel={channel._id}
-				replies={replies}
-				setReplies={setReplies}
-			/>
-			<Base>
-				{permissions & ChannelPermission.UploadFiles ? (
-					<Action>
-						<FileUploader
-							size={24}
-							behaviour="multi"
-							style="attachment"
-							fileType="attachments"
-							maxFileSize={20_000_000}
-							attached={uploadState.type !== "none"}
-							uploading={
-								uploadState.type === "uploading" ||
-								uploadState.type === "sending"
-							}
-							remove={async () =>
-								setUploadState({ type: "none" })
-							}
-							onChange={(files) =>
-								setUploadState({ type: "attached", files })
-							}
-							cancel={() =>
-								uploadState.type === "uploading" &&
-								uploadState.cancel.cancel("cancel")
-							}
-							append={(files) => {
-								if (files.length === 0) return;
-
-								if (uploadState.type === "none") {
-									setUploadState({ type: "attached", files });
-								} else if (uploadState.type === "attached") {
-									setUploadState({
-										type: "attached",
-										files: [...uploadState.files, ...files],
-									});
-								}
-							}}
-						/>
-					</Action>
-				) : undefined}
-				<TextAreaAutoSize
-					autoFocus
-					hideBorder
-					maxRows={5}
-					padding={14}
-					id="message"
-					value={draft ?? ""}
-					onKeyUp={onKeyUp}
-					onKeyDown={(e) => {
-						if (onKeyDown(e)) return;
-
-						if (
-							e.key === "ArrowUp" &&
-							(!draft || draft.length === 0)
-						) {
-							e.preventDefault();
-							internalEmit("MessageRenderer", "edit_last");
-							return;
-						}
-
-						if (
-							!e.shiftKey &&
-							e.key === "Enter" &&
-							!isTouchscreenDevice
-						) {
-							e.preventDefault();
-							return send();
-						}
-
-						debouncedStopTyping(true);
-					}}
-					placeholder={
-						channel.channel_type === "DirectMessage"
-							? translate("app.main.channel.message_who", {
-									person: client.users.get(
-										client.channels.getRecipient(
-											channel._id,
-										),
-									)?.username,
-							  })
-							: channel.channel_type === "SavedMessages"
-							? translate("app.main.channel.message_saved")
-							: translate("app.main.channel.message_where", {
-									channel_name: channel.name,
-							  })
-					}
-					disabled={
-						uploadState.type === "uploading" ||
-						uploadState.type === "sending"
-					}
-					onChange={(e) => {
-						setMessage(e.currentTarget.value);
-						startTyping();
-						onChange(e);
-					}}
-					onFocus={onFocus}
-					onBlur={onBlur}
-				/>
-				{isTouchscreenDevice && (
-					<Action>
-						<IconButton onClick={send}>
-							<Send size={20} />
-						</IconButton>
-					</Action>
-				)}
-			</Base>
-		</>
-	);
+    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();
+
+    const permissions = useChannelPermission(channel._id);
+    if (!(permissions & ChannelPermission.SendMessage)) {
+        return (
+            <Base>
+                <Blocked>
+                    <PermissionTooltip
+                        permission="SendMessages"
+                        placement="top">
+                        <ShieldX size={22} />
+                    </PermissionTooltip>
+                    <Text id="app.main.channel.misc.no_sending" />
+                </Blocked>
+            </Base>
+        );
+    }
+
+    function setMessage(content?: string) {
+        if (content) {
+            dispatch({
+                type: "SET_DRAFT",
+                channel: channel._id,
+                content,
+            });
+        } else {
+            dispatch({
+                type: "CLEAR_DRAFT",
+                channel: channel._id,
+            });
+        }
+    }
+
+    useEffect(() => {
+        function append(content: string, action: "quote" | "mention") {
+            const text =
+                action === "quote"
+                    ? `${content
+                          .split("\n")
+                          .map((x) => `> ${x}`)
+                          .join("\n")}\n\n`
+                    : `${content} `;
+
+            if (!draft || draft.length === 0) {
+                setMessage(text);
+            } else {
+                setMessage(`${draft}\n${text}`);
+            }
+        }
+
+        return internalSubscribe("MessageBox", "append", append);
+    }, [draft]);
+
+    async function send() {
+        if (uploadState.type === "uploading" || uploadState.type === "sending")
+            return;
+
+        const content = draft?.trim() ?? "";
+        if (uploadState.type === "attached") return sendFile(content);
+        if (content.length === 0) return;
+
+        stopTyping();
+        setMessage();
+        setReplies([]);
+        playSound("outbound");
+
+        const nonce = ulid();
+        dispatch({
+            type: "QUEUE_ADD",
+            nonce,
+            channel: channel._id,
+            message: {
+                _id: nonce,
+                channel: channel._id,
+                author: client.user!._id,
+
+                content,
+                replies,
+            },
+        });
+
+        defer(() =>
+            SingletonMessageRenderer.jumpToBottom(
+                channel._id,
+                SMOOTH_SCROLL_ON_RECEIVE,
+            ),
+        );
+
+        try {
+            await client.channels.sendMessage(channel._id, {
+                content,
+                nonce,
+                replies,
+            });
+        } catch (error) {
+            dispatch({
+                type: "QUEUE_FAIL",
+                error: takeError(error),
+                nonce,
+            });
+        }
+    }
+
+    async function sendFile(content: string) {
+        if (uploadState.type !== "attached") return;
+        let attachments: string[] = [];
+
+        const cancel = Axios.CancelToken.source();
+        const files = uploadState.files;
+        stopTyping();
+        setUploadState({ type: "uploading", files, percent: 0, cancel });
+
+        try {
+            for (let i = 0; i < files.length && i < CAN_UPLOAD_AT_ONCE; i++) {
+                const file = files[i];
+                attachments.push(
+                    await uploadFile(
+                        client.configuration!.features.autumn.url,
+                        "attachments",
+                        file,
+                        {
+                            onUploadProgress: (e) =>
+                                setUploadState({
+                                    type: "uploading",
+                                    files,
+                                    percent: Math.round(
+                                        (i * 100 + (100 * e.loaded) / e.total) /
+                                            Math.min(
+                                                files.length,
+                                                CAN_UPLOAD_AT_ONCE,
+                                            ),
+                                    ),
+                                    cancel,
+                                }),
+                            cancelToken: cancel.token,
+                        },
+                    ),
+                );
+            }
+        } catch (err) {
+            if (err?.message === "cancel") {
+                setUploadState({
+                    type: "attached",
+                    files,
+                });
+            } else {
+                setUploadState({
+                    type: "failed",
+                    files,
+                    error: takeError(err),
+                });
+            }
+
+            return;
+        }
+
+        setUploadState({
+            type: "sending",
+            files,
+        });
+
+        const nonce = ulid();
+        try {
+            await client.channels.sendMessage(channel._id, {
+                content,
+                nonce,
+                replies,
+                attachments,
+            });
+        } catch (err) {
+            setUploadState({
+                type: "failed",
+                files,
+                error: takeError(err),
+            });
+
+            return;
+        }
+
+        setMessage();
+        setReplies([]);
+        playSound("outbound");
+
+        if (files.length > CAN_UPLOAD_AT_ONCE) {
+            setUploadState({
+                type: "attached",
+                files: files.slice(CAN_UPLOAD_AT_ONCE),
+            });
+        } else {
+            setUploadState({ type: "none" });
+        }
+    }
+
+    function startTyping() {
+        if (typeof typing === "number" && +new Date() < typing) return;
+
+        const ws = client.websocket;
+        if (ws.connected) {
+            setTyping(+new Date() + 4000);
+            ws.send({
+                type: "BeginTyping",
+                channel: channel._id,
+            });
+        }
+    }
+
+    function stopTyping(force?: boolean) {
+        if (force || typing) {
+            const ws = client.websocket;
+            if (ws.connected) {
+                setTyping(false);
+                ws.send({
+                    type: "EndTyping",
+                    channel: channel._id,
+                });
+            }
+        }
+    }
+
+    const debouncedStopTyping = useCallback(debounce(stopTyping, 1000), [
+        channel._id,
+    ]);
+    const {
+        onChange,
+        onKeyUp,
+        onKeyDown,
+        onFocus,
+        onBlur,
+        ...autoCompleteProps
+    } = useAutoComplete(setMessage, {
+        users: { type: "channel", id: channel._id },
+        channels:
+            channel.channel_type === "TextChannel"
+                ? { server: channel.server }
+                : undefined,
+    });
+
+    return (
+        <>
+            <AutoComplete {...autoCompleteProps} />
+            <FilePreview
+                state={uploadState}
+                addFile={() =>
+                    uploadState.type === "attached" &&
+                    grabFiles(
+                        20_000_000,
+                        (files) =>
+                            setUploadState({
+                                type: "attached",
+                                files: [...uploadState.files, ...files],
+                            }),
+                        () =>
+                            openScreen({ id: "error", error: "FileTooLarge" }),
+                        true,
+                    )
+                }
+                removeFile={(index) => {
+                    if (uploadState.type !== "attached") return;
+                    if (uploadState.files.length === 1) {
+                        setUploadState({ type: "none" });
+                    } else {
+                        setUploadState({
+                            type: "attached",
+                            files: uploadState.files.filter(
+                                (_, i) => index !== i,
+                            ),
+                        });
+                    }
+                }}
+            />
+            <ReplyBar
+                channel={channel._id}
+                replies={replies}
+                setReplies={setReplies}
+            />
+            <Base>
+                {permissions & ChannelPermission.UploadFiles ? (
+                    <Action>
+                        <FileUploader
+                            size={24}
+                            behaviour="multi"
+                            style="attachment"
+                            fileType="attachments"
+                            maxFileSize={20_000_000}
+                            attached={uploadState.type !== "none"}
+                            uploading={
+                                uploadState.type === "uploading" ||
+                                uploadState.type === "sending"
+                            }
+                            remove={async () =>
+                                setUploadState({ type: "none" })
+                            }
+                            onChange={(files) =>
+                                setUploadState({ type: "attached", files })
+                            }
+                            cancel={() =>
+                                uploadState.type === "uploading" &&
+                                uploadState.cancel.cancel("cancel")
+                            }
+                            append={(files) => {
+                                if (files.length === 0) return;
+
+                                if (uploadState.type === "none") {
+                                    setUploadState({ type: "attached", files });
+                                } else if (uploadState.type === "attached") {
+                                    setUploadState({
+                                        type: "attached",
+                                        files: [...uploadState.files, ...files],
+                                    });
+                                }
+                            }}
+                        />
+                    </Action>
+                ) : undefined}
+                <TextAreaAutoSize
+                    autoFocus
+                    hideBorder
+                    maxRows={5}
+                    padding={14}
+                    id="message"
+                    value={draft ?? ""}
+                    onKeyUp={onKeyUp}
+                    onKeyDown={(e) => {
+                        if (onKeyDown(e)) return;
+
+                        if (
+                            e.key === "ArrowUp" &&
+                            (!draft || draft.length === 0)
+                        ) {
+                            e.preventDefault();
+                            internalEmit("MessageRenderer", "edit_last");
+                            return;
+                        }
+
+                        if (
+                            !e.shiftKey &&
+                            e.key === "Enter" &&
+                            !isTouchscreenDevice
+                        ) {
+                            e.preventDefault();
+                            return send();
+                        }
+
+                        debouncedStopTyping(true);
+                    }}
+                    placeholder={
+                        channel.channel_type === "DirectMessage"
+                            ? translate("app.main.channel.message_who", {
+                                  person: client.users.get(
+                                      client.channels.getRecipient(channel._id),
+                                  )?.username,
+                              })
+                            : channel.channel_type === "SavedMessages"
+                            ? translate("app.main.channel.message_saved")
+                            : translate("app.main.channel.message_where", {
+                                  channel_name: channel.name,
+                              })
+                    }
+                    disabled={
+                        uploadState.type === "uploading" ||
+                        uploadState.type === "sending"
+                    }
+                    onChange={(e) => {
+                        setMessage(e.currentTarget.value);
+                        startTyping();
+                        onChange(e);
+                    }}
+                    onFocus={onFocus}
+                    onBlur={onBlur}
+                />
+                {isTouchscreenDevice && (
+                    <Action>
+                        <IconButton onClick={send}>
+                            <Send size={20} />
+                        </IconButton>
+                    </Action>
+                )}
+            </Base>
+        </>
+    );
 }
 
 export default connectState<Omit<Props, "dispatch" | "draft">>(
-	MessageBox,
-	(state, { channel }) => {
-		return {
-			draft: state.drafts[channel._id],
-		};
-	},
-	true,
+    MessageBox,
+    (state, { channel }) => {
+        return {
+            draft: state.drafts[channel._id],
+        };
+    },
+    true,
 );
diff --git a/src/components/common/messaging/SystemMessage.tsx b/src/components/common/messaging/SystemMessage.tsx
index ecd78dc..c082292 100644
--- a/src/components/common/messaging/SystemMessage.tsx
+++ b/src/components/common/messaging/SystemMessage.tsx
@@ -12,149 +12,149 @@ import UserShort from "../user/UserShort";
 import MessageBase, { MessageDetail, MessageInfo } from "./MessageBase";
 
 const SystemContent = styled.div`
-	gap: 4px;
-	display: flex;
-	padding: 2px 0;
-	flex-wrap: wrap;
-	align-items: center;
-	flex-direction: row;
+    gap: 4px;
+    display: flex;
+    padding: 2px 0;
+    flex-wrap: wrap;
+    align-items: center;
+    flex-direction: row;
 `;
 
 type SystemMessageParsed =
-	| { type: "text"; content: string }
-	| { type: "user_added"; user: User; by: User }
-	| { type: "user_remove"; user: User; by: User }
-	| { type: "user_joined"; user: User }
-	| { type: "user_left"; user: User }
-	| { type: "user_kicked"; user: User }
-	| { type: "user_banned"; user: User }
-	| { type: "channel_renamed"; name: string; by: User }
-	| { type: "channel_description_changed"; by: User }
-	| { type: "channel_icon_changed"; by: User };
+    | { type: "text"; content: string }
+    | { type: "user_added"; user: User; by: User }
+    | { type: "user_remove"; user: User; by: User }
+    | { type: "user_joined"; user: User }
+    | { type: "user_left"; user: User }
+    | { type: "user_kicked"; user: User }
+    | { type: "user_banned"; user: User }
+    | { type: "channel_renamed"; name: string; by: User }
+    | { type: "channel_description_changed"; by: User }
+    | { type: "channel_icon_changed"; by: User };
 
 interface Props {
-	attachContext?: boolean;
-	message: MessageObject;
+    attachContext?: boolean;
+    message: MessageObject;
 }
 
 export function SystemMessage({ attachContext, message }: Props) {
-	const ctx = useForceUpdate();
+    const ctx = useForceUpdate();
 
-	let data: SystemMessageParsed;
-	let content = message.content;
-	if (typeof content === "object") {
-		switch (content.type) {
-			case "text":
-				data = content;
-				break;
-			case "user_added":
-			case "user_remove":
-				data = {
-					type: content.type,
-					user: useUser(content.id, ctx) as User,
-					by: useUser(content.by, ctx) as User,
-				};
-				break;
-			case "user_joined":
-			case "user_left":
-			case "user_kicked":
-			case "user_banned":
-				data = {
-					type: content.type,
-					user: useUser(content.id, ctx) as User,
-				};
-				break;
-			case "channel_renamed":
-				data = {
-					type: "channel_renamed",
-					name: content.name,
-					by: useUser(content.by, ctx) as User,
-				};
-				break;
-			case "channel_description_changed":
-			case "channel_icon_changed":
-				data = {
-					type: content.type,
-					by: useUser(content.by, ctx) as User,
-				};
-				break;
-			default:
-				data = { type: "text", content: JSON.stringify(content) };
-		}
-	} else {
-		data = { type: "text", content };
-	}
+    let data: SystemMessageParsed;
+    let content = message.content;
+    if (typeof content === "object") {
+        switch (content.type) {
+            case "text":
+                data = content;
+                break;
+            case "user_added":
+            case "user_remove":
+                data = {
+                    type: content.type,
+                    user: useUser(content.id, ctx) as User,
+                    by: useUser(content.by, ctx) as User,
+                };
+                break;
+            case "user_joined":
+            case "user_left":
+            case "user_kicked":
+            case "user_banned":
+                data = {
+                    type: content.type,
+                    user: useUser(content.id, ctx) as User,
+                };
+                break;
+            case "channel_renamed":
+                data = {
+                    type: "channel_renamed",
+                    name: content.name,
+                    by: useUser(content.by, ctx) as User,
+                };
+                break;
+            case "channel_description_changed":
+            case "channel_icon_changed":
+                data = {
+                    type: content.type,
+                    by: useUser(content.by, ctx) as User,
+                };
+                break;
+            default:
+                data = { type: "text", content: JSON.stringify(content) };
+        }
+    } else {
+        data = { type: "text", content };
+    }
 
-	let children;
-	switch (data.type) {
-		case "text":
-			children = <span>{data.content}</span>;
-			break;
-		case "user_added":
-		case "user_remove":
-			children = (
-				<TextReact
-					id={`app.main.channel.system.${
-						data.type === "user_added" ? "added_by" : "removed_by"
-					}`}
-					fields={{
-						user: <UserShort user={data.user} />,
-						other_user: <UserShort user={data.by} />,
-					}}
-				/>
-			);
-			break;
-		case "user_joined":
-		case "user_left":
-		case "user_kicked":
-		case "user_banned":
-			children = (
-				<TextReact
-					id={`app.main.channel.system.${data.type}`}
-					fields={{
-						user: <UserShort user={data.user} />,
-					}}
-				/>
-			);
-			break;
-		case "channel_renamed":
-			children = (
-				<TextReact
-					id={`app.main.channel.system.channel_renamed`}
-					fields={{
-						user: <UserShort user={data.by} />,
-						name: <b>{data.name}</b>,
-					}}
-				/>
-			);
-			break;
-		case "channel_description_changed":
-		case "channel_icon_changed":
-			children = (
-				<TextReact
-					id={`app.main.channel.system.${data.type}`}
-					fields={{
-						user: <UserShort user={data.by} />,
-					}}
-				/>
-			);
-			break;
-	}
+    let children;
+    switch (data.type) {
+        case "text":
+            children = <span>{data.content}</span>;
+            break;
+        case "user_added":
+        case "user_remove":
+            children = (
+                <TextReact
+                    id={`app.main.channel.system.${
+                        data.type === "user_added" ? "added_by" : "removed_by"
+                    }`}
+                    fields={{
+                        user: <UserShort user={data.user} />,
+                        other_user: <UserShort user={data.by} />,
+                    }}
+                />
+            );
+            break;
+        case "user_joined":
+        case "user_left":
+        case "user_kicked":
+        case "user_banned":
+            children = (
+                <TextReact
+                    id={`app.main.channel.system.${data.type}`}
+                    fields={{
+                        user: <UserShort user={data.user} />,
+                    }}
+                />
+            );
+            break;
+        case "channel_renamed":
+            children = (
+                <TextReact
+                    id={`app.main.channel.system.channel_renamed`}
+                    fields={{
+                        user: <UserShort user={data.by} />,
+                        name: <b>{data.name}</b>,
+                    }}
+                />
+            );
+            break;
+        case "channel_description_changed":
+        case "channel_icon_changed":
+            children = (
+                <TextReact
+                    id={`app.main.channel.system.${data.type}`}
+                    fields={{
+                        user: <UserShort user={data.by} />,
+                    }}
+                />
+            );
+            break;
+    }
 
-	return (
-		<MessageBase
-			onContextMenu={
-				attachContext
-					? attachContextMenu("Menu", {
-							message,
-							contextualChannel: message.channel,
-					  })
-					: undefined
-			}>
-			<MessageInfo>
-				<MessageDetail message={message} position="left" />
-			</MessageInfo>
-			<SystemContent>{children}</SystemContent>
-		</MessageBase>
-	);
+    return (
+        <MessageBase
+            onContextMenu={
+                attachContext
+                    ? attachContextMenu("Menu", {
+                          message,
+                          contextualChannel: message.channel,
+                      })
+                    : undefined
+            }>
+            <MessageInfo>
+                <MessageDetail message={message} position="left" />
+            </MessageInfo>
+            <SystemContent>{children}</SystemContent>
+        </MessageBase>
+    );
 }
diff --git a/src/components/common/messaging/attachments/Attachment.tsx b/src/components/common/messaging/attachments/Attachment.tsx
index 4f88feb..a19eea4 100644
--- a/src/components/common/messaging/attachments/Attachment.tsx
+++ b/src/components/common/messaging/attachments/Attachment.tsx
@@ -13,121 +13,121 @@ import AttachmentActions from "./AttachmentActions";
 import TextFile from "./TextFile";
 
 interface Props {
-	attachment: AttachmentRJS;
-	hasContent: boolean;
+    attachment: AttachmentRJS;
+    hasContent: boolean;
 }
 
 const MAX_ATTACHMENT_WIDTH = 480;
 
 export default function Attachment({ attachment, hasContent }: Props) {
-	const client = useContext(AppContext);
-	const { openScreen } = useIntermediate();
-	const { filename, metadata } = attachment;
-	const [spoiler, setSpoiler] = useState(filename.startsWith("SPOILER_"));
-	const [loaded, setLoaded] = useState(false);
+    const client = useContext(AppContext);
+    const { openScreen } = useIntermediate();
+    const { filename, metadata } = attachment;
+    const [spoiler, setSpoiler] = useState(filename.startsWith("SPOILER_"));
+    const [loaded, setLoaded] = useState(false);
 
-	const url = client.generateFileURL(
-		attachment,
-		{ width: MAX_ATTACHMENT_WIDTH * 1.5 },
-		true,
-	);
+    const url = client.generateFileURL(
+        attachment,
+        { width: MAX_ATTACHMENT_WIDTH * 1.5 },
+        true,
+    );
 
-	switch (metadata.type) {
-		case "Image": {
-			return (
-				<div
-					className={styles.container}
-					onClick={() => spoiler && setSpoiler(false)}>
-					{spoiler && (
-						<div className={styles.overflow}>
-							<span>
-								<Text id="app.main.channel.misc.spoiler_attachment" />
-							</span>
-						</div>
-					)}
-					<img
-						src={url}
-						alt={filename}
-						width={metadata.width}
-						height={metadata.height}
-						data-spoiler={spoiler}
-						data-has-content={hasContent}
-						className={classNames(
-							styles.attachment,
-							styles.image,
-							loaded && styles.loaded,
-						)}
-						onClick={() =>
-							openScreen({ id: "image_viewer", attachment })
-						}
-						onMouseDown={(ev) =>
-							ev.button === 1 && window.open(url, "_blank")
-						}
-						onLoad={() => setLoaded(true)}
-					/>
-				</div>
-			);
-		}
-		case "Audio": {
-			return (
-				<div
-					className={classNames(styles.attachment, styles.audio)}
-					data-has-content={hasContent}>
-					<AttachmentActions attachment={attachment} />
-					<audio src={url} controls />
-				</div>
-			);
-		}
-		case "Video": {
-			return (
-				<div
-					className={styles.container}
-					onClick={() => spoiler && setSpoiler(false)}>
-					{spoiler && (
-						<div className={styles.overflow}>
-							<span>
-								<Text id="app.main.channel.misc.spoiler_attachment" />
-							</span>
-						</div>
-					)}
-					<div
-						data-spoiler={spoiler}
-						data-has-content={hasContent}
-						className={classNames(styles.attachment, styles.video)}>
-						<AttachmentActions attachment={attachment} />
-						<video
-							src={url}
-							width={metadata.width}
-							height={metadata.height}
-							className={classNames(loaded && styles.loaded)}
-							controls
-							onMouseDown={(ev) =>
-								ev.button === 1 && window.open(url, "_blank")
-							}
-							onLoadedMetadata={() => setLoaded(true)}
-						/>
-					</div>
-				</div>
-			);
-		}
-		case "Text": {
-			return (
-				<div
-					className={classNames(styles.attachment, styles.text)}
-					data-has-content={hasContent}>
-					<TextFile attachment={attachment} />
-					<AttachmentActions attachment={attachment} />
-				</div>
-			);
-		}
-		default: {
-			return (
-				<div
-					className={classNames(styles.attachment, styles.file)}
-					data-has-content={hasContent}>
-					<AttachmentActions attachment={attachment} />
-				</div>
-			);
-		}
-	}
+    switch (metadata.type) {
+        case "Image": {
+            return (
+                <div
+                    className={styles.container}
+                    onClick={() => spoiler && setSpoiler(false)}>
+                    {spoiler && (
+                        <div className={styles.overflow}>
+                            <span>
+                                <Text id="app.main.channel.misc.spoiler_attachment" />
+                            </span>
+                        </div>
+                    )}
+                    <img
+                        src={url}
+                        alt={filename}
+                        width={metadata.width}
+                        height={metadata.height}
+                        data-spoiler={spoiler}
+                        data-has-content={hasContent}
+                        className={classNames(
+                            styles.attachment,
+                            styles.image,
+                            loaded && styles.loaded,
+                        )}
+                        onClick={() =>
+                            openScreen({ id: "image_viewer", attachment })
+                        }
+                        onMouseDown={(ev) =>
+                            ev.button === 1 && window.open(url, "_blank")
+                        }
+                        onLoad={() => setLoaded(true)}
+                    />
+                </div>
+            );
+        }
+        case "Audio": {
+            return (
+                <div
+                    className={classNames(styles.attachment, styles.audio)}
+                    data-has-content={hasContent}>
+                    <AttachmentActions attachment={attachment} />
+                    <audio src={url} controls />
+                </div>
+            );
+        }
+        case "Video": {
+            return (
+                <div
+                    className={styles.container}
+                    onClick={() => spoiler && setSpoiler(false)}>
+                    {spoiler && (
+                        <div className={styles.overflow}>
+                            <span>
+                                <Text id="app.main.channel.misc.spoiler_attachment" />
+                            </span>
+                        </div>
+                    )}
+                    <div
+                        data-spoiler={spoiler}
+                        data-has-content={hasContent}
+                        className={classNames(styles.attachment, styles.video)}>
+                        <AttachmentActions attachment={attachment} />
+                        <video
+                            src={url}
+                            width={metadata.width}
+                            height={metadata.height}
+                            className={classNames(loaded && styles.loaded)}
+                            controls
+                            onMouseDown={(ev) =>
+                                ev.button === 1 && window.open(url, "_blank")
+                            }
+                            onLoadedMetadata={() => setLoaded(true)}
+                        />
+                    </div>
+                </div>
+            );
+        }
+        case "Text": {
+            return (
+                <div
+                    className={classNames(styles.attachment, styles.text)}
+                    data-has-content={hasContent}>
+                    <TextFile attachment={attachment} />
+                    <AttachmentActions attachment={attachment} />
+                </div>
+            );
+        }
+        default: {
+            return (
+                <div
+                    className={classNames(styles.attachment, styles.file)}
+                    data-has-content={hasContent}>
+                    <AttachmentActions attachment={attachment} />
+                </div>
+            );
+        }
+    }
 }
diff --git a/src/components/common/messaging/attachments/AttachmentActions.tsx b/src/components/common/messaging/attachments/AttachmentActions.tsx
index b5458c9..c551d2b 100644
--- a/src/components/common/messaging/attachments/AttachmentActions.tsx
+++ b/src/components/common/messaging/attachments/AttachmentActions.tsx
@@ -1,9 +1,9 @@
 import {
-	Download,
-	LinkExternal,
-	File,
-	Headphone,
-	Video,
+    Download,
+    LinkExternal,
+    File,
+    Headphone,
+    Video,
 } from "@styled-icons/boxicons-regular";
 import { Attachment } from "revolt.js/dist/api/objects";
 
@@ -18,98 +18,98 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
 import IconButton from "../../../ui/IconButton";
 
 interface Props {
-	attachment: Attachment;
+    attachment: Attachment;
 }
 
 export default function AttachmentActions({ attachment }: Props) {
-	const client = useContext(AppContext);
-	const { filename, metadata, size } = attachment;
+    const client = useContext(AppContext);
+    const { filename, metadata, size } = attachment;
 
-	const url = client.generateFileURL(attachment)!;
-	const open_url = `${url}/${filename}`;
-	const download_url = url.replace("attachments", "attachments/download");
+    const url = client.generateFileURL(attachment)!;
+    const open_url = `${url}/${filename}`;
+    const download_url = url.replace("attachments", "attachments/download");
 
-	const filesize = determineFileSize(size);
+    const filesize = determineFileSize(size);
 
-	switch (metadata.type) {
-		case "Image":
-			return (
-				<div className={classNames(styles.actions, styles.imageAction)}>
-					<span className={styles.filename}>{filename}</span>
-					<span className={styles.filesize}>
-						{metadata.width + "x" + metadata.height} ({filesize})
-					</span>
-					<a
-						href={open_url}
-						target="_blank"
-						className={styles.iconType}>
-						<IconButton>
-							<LinkExternal size={24} />
-						</IconButton>
-					</a>
-					<a
-						href={download_url}
-						className={styles.downloadIcon}
-						download
-						target="_blank">
-						<IconButton>
-							<Download size={24} />
-						</IconButton>
-					</a>
-				</div>
-			);
-		case "Audio":
-			return (
-				<div className={classNames(styles.actions, styles.audioAction)}>
-					<Headphone size={24} className={styles.iconType} />
-					<span className={styles.filename}>{filename}</span>
-					<span className={styles.filesize}>{filesize}</span>
-					<a
-						href={download_url}
-						className={styles.downloadIcon}
-						download
-						target="_blank">
-						<IconButton>
-							<Download size={24} />
-						</IconButton>
-					</a>
-				</div>
-			);
-		case "Video":
-			return (
-				<div className={classNames(styles.actions, styles.videoAction)}>
-					<Video size={24} className={styles.iconType} />
-					<span className={styles.filename}>{filename}</span>
-					<span className={styles.filesize}>
-						{metadata.width + "x" + metadata.height} ({filesize})
-					</span>
-					<a
-						href={download_url}
-						className={styles.downloadIcon}
-						download
-						target="_blank">
-						<IconButton>
-							<Download size={24} />
-						</IconButton>
-					</a>
-				</div>
-			);
-		default:
-			return (
-				<div className={styles.actions}>
-					<File size={24} className={styles.iconType} />
-					<span className={styles.filename}>{filename}</span>
-					<span className={styles.filesize}>{filesize}</span>
-					<a
-						href={download_url}
-						className={styles.downloadIcon}
-						download
-						target="_blank">
-						<IconButton>
-							<Download size={24} />
-						</IconButton>
-					</a>
-				</div>
-			);
-	}
+    switch (metadata.type) {
+        case "Image":
+            return (
+                <div className={classNames(styles.actions, styles.imageAction)}>
+                    <span className={styles.filename}>{filename}</span>
+                    <span className={styles.filesize}>
+                        {metadata.width + "x" + metadata.height} ({filesize})
+                    </span>
+                    <a
+                        href={open_url}
+                        target="_blank"
+                        className={styles.iconType}>
+                        <IconButton>
+                            <LinkExternal size={24} />
+                        </IconButton>
+                    </a>
+                    <a
+                        href={download_url}
+                        className={styles.downloadIcon}
+                        download
+                        target="_blank">
+                        <IconButton>
+                            <Download size={24} />
+                        </IconButton>
+                    </a>
+                </div>
+            );
+        case "Audio":
+            return (
+                <div className={classNames(styles.actions, styles.audioAction)}>
+                    <Headphone size={24} className={styles.iconType} />
+                    <span className={styles.filename}>{filename}</span>
+                    <span className={styles.filesize}>{filesize}</span>
+                    <a
+                        href={download_url}
+                        className={styles.downloadIcon}
+                        download
+                        target="_blank">
+                        <IconButton>
+                            <Download size={24} />
+                        </IconButton>
+                    </a>
+                </div>
+            );
+        case "Video":
+            return (
+                <div className={classNames(styles.actions, styles.videoAction)}>
+                    <Video size={24} className={styles.iconType} />
+                    <span className={styles.filename}>{filename}</span>
+                    <span className={styles.filesize}>
+                        {metadata.width + "x" + metadata.height} ({filesize})
+                    </span>
+                    <a
+                        href={download_url}
+                        className={styles.downloadIcon}
+                        download
+                        target="_blank">
+                        <IconButton>
+                            <Download size={24} />
+                        </IconButton>
+                    </a>
+                </div>
+            );
+        default:
+            return (
+                <div className={styles.actions}>
+                    <File size={24} className={styles.iconType} />
+                    <span className={styles.filename}>{filename}</span>
+                    <span className={styles.filesize}>{filesize}</span>
+                    <a
+                        href={download_url}
+                        className={styles.downloadIcon}
+                        download
+                        target="_blank">
+                        <IconButton>
+                            <Download size={24} />
+                        </IconButton>
+                    </a>
+                </div>
+            );
+    }
 }
diff --git a/src/components/common/messaging/attachments/MessageReply.tsx b/src/components/common/messaging/attachments/MessageReply.tsx
index 4a28383..0620b90 100644
--- a/src/components/common/messaging/attachments/MessageReply.tsx
+++ b/src/components/common/messaging/attachments/MessageReply.tsx
@@ -11,83 +11,83 @@ import Markdown from "../../../markdown/Markdown";
 import UserShort from "../../user/UserShort";
 
 interface Props {
-	channel: string;
-	index: number;
-	id: string;
+    channel: string;
+    index: number;
+    id: string;
 }
 
 export const ReplyBase = styled.div<{
-	head?: boolean;
-	fail?: boolean;
-	preview?: boolean;
+    head?: boolean;
+    fail?: boolean;
+    preview?: boolean;
 }>`
-	gap: 4px;
-	display: flex;
-	font-size: 0.8em;
-	margin-left: 30px;
-	user-select: none;
-	margin-bottom: 4px;
-	align-items: center;
-	color: var(--secondary-foreground);
+    gap: 4px;
+    display: flex;
+    font-size: 0.8em;
+    margin-left: 30px;
+    user-select: none;
+    margin-bottom: 4px;
+    align-items: center;
+    color: var(--secondary-foreground);
 
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
 
-	svg:first-child {
-		flex-shrink: 0;
-		transform: scaleX(-1);
-		color: var(--tertiary-foreground);
-	}
+    svg:first-child {
+        flex-shrink: 0;
+        transform: scaleX(-1);
+        color: var(--tertiary-foreground);
+    }
 
-	${(props) =>
-		props.fail &&
-		css`
-			color: var(--tertiary-foreground);
-		`}
+    ${(props) =>
+        props.fail &&
+        css`
+            color: var(--tertiary-foreground);
+        `}
 
-	${(props) =>
-		props.head &&
-		css`
-			margin-top: 12px;
-		`}
+    ${(props) =>
+        props.head &&
+        css`
+            margin-top: 12px;
+        `}
 
     ${(props) =>
-		props.preview &&
-		css`
-			margin-left: 0;
-		`}
+        props.preview &&
+        css`
+            margin-left: 0;
+        `}
 `;
 
 export function MessageReply({ index, channel, id }: Props) {
-	const view = useRenderState(channel);
-	if (view?.type !== "RENDER") return null;
+    const view = useRenderState(channel);
+    if (view?.type !== "RENDER") return null;
 
-	const message = view.messages.find((x) => x._id === id);
-	if (!message) {
-		return (
-			<ReplyBase head={index === 0} fail>
-				<Reply size={16} />
-				<span>
-					<Text id="app.main.channel.misc.failed_load" />
-				</span>
-			</ReplyBase>
-		);
-	}
+    const message = view.messages.find((x) => x._id === id);
+    if (!message) {
+        return (
+            <ReplyBase head={index === 0} fail>
+                <Reply size={16} />
+                <span>
+                    <Text id="app.main.channel.misc.failed_load" />
+                </span>
+            </ReplyBase>
+        );
+    }
 
-	const user = useUser(message.author);
+    const user = useUser(message.author);
 
-	return (
-		<ReplyBase head={index === 0}>
-			<Reply size={16} />
-			<UserShort user={user} size={16} />
-			{message.attachments && message.attachments.length > 0 && (
-				<File size={16} />
-			)}
-			<Markdown
-				disallowBigEmoji
-				content={(message.content as string).replace(/\n/g, " ")}
-			/>
-		</ReplyBase>
-	);
+    return (
+        <ReplyBase head={index === 0}>
+            <Reply size={16} />
+            <UserShort user={user} size={16} />
+            {message.attachments && message.attachments.length > 0 && (
+                <File size={16} />
+            )}
+            <Markdown
+                disallowBigEmoji
+                content={(message.content as string).replace(/\n/g, " ")}
+            />
+        </ReplyBase>
+    );
 }
diff --git a/src/components/common/messaging/attachments/TextFile.tsx b/src/components/common/messaging/attachments/TextFile.tsx
index a31d3d9..f8ad00e 100644
--- a/src/components/common/messaging/attachments/TextFile.tsx
+++ b/src/components/common/messaging/attachments/TextFile.tsx
@@ -6,67 +6,67 @@ import { useContext, useEffect, useState } from "preact/hooks";
 
 import RequiresOnline from "../../../../context/revoltjs/RequiresOnline";
 import {
-	AppContext,
-	StatusContext,
+    AppContext,
+    StatusContext,
 } from "../../../../context/revoltjs/RevoltClient";
 
 import Preloader from "../../../ui/Preloader";
 
 interface Props {
-	attachment: Attachment;
+    attachment: Attachment;
 }
 
 const fileCache: { [key: string]: string } = {};
 
 export default function TextFile({ attachment }: Props) {
-	const [content, setContent] = useState<undefined | string>(undefined);
-	const [loading, setLoading] = useState(false);
-	const status = useContext(StatusContext);
-	const client = useContext(AppContext);
+    const [content, setContent] = useState<undefined | string>(undefined);
+    const [loading, setLoading] = useState(false);
+    const status = useContext(StatusContext);
+    const client = useContext(AppContext);
 
-	const url = client.generateFileURL(attachment)!;
+    const url = client.generateFileURL(attachment)!;
 
-	useEffect(() => {
-		if (typeof content !== "undefined") return;
-		if (loading) return;
-		setLoading(true);
+    useEffect(() => {
+        if (typeof content !== "undefined") return;
+        if (loading) return;
+        setLoading(true);
 
-		let cached = fileCache[attachment._id];
-		if (cached) {
-			setContent(cached);
-			setLoading(false);
-		} else {
-			axios
-				.get(url)
-				.then((res) => {
-					setContent(res.data);
-					fileCache[attachment._id] = res.data;
-					setLoading(false);
-				})
-				.catch(() => {
-					console.error(
-						"Failed to load text file. [",
-						attachment._id,
-						"]",
-					);
-					setLoading(false);
-				});
-		}
-	}, [content, loading, status]);
+        let cached = fileCache[attachment._id];
+        if (cached) {
+            setContent(cached);
+            setLoading(false);
+        } else {
+            axios
+                .get(url)
+                .then((res) => {
+                    setContent(res.data);
+                    fileCache[attachment._id] = res.data;
+                    setLoading(false);
+                })
+                .catch(() => {
+                    console.error(
+                        "Failed to load text file. [",
+                        attachment._id,
+                        "]",
+                    );
+                    setLoading(false);
+                });
+        }
+    }, [content, loading, status]);
 
-	return (
-		<div
-			className={styles.textContent}
-			data-loading={typeof content === "undefined"}>
-			{content ? (
-				<pre>
-					<code>{content}</code>
-				</pre>
-			) : (
-				<RequiresOnline>
-					<Preloader type="ring" />
-				</RequiresOnline>
-			)}
-		</div>
-	);
+    return (
+        <div
+            className={styles.textContent}
+            data-loading={typeof content === "undefined"}>
+            {content ? (
+                <pre>
+                    <code>{content}</code>
+                </pre>
+            ) : (
+                <RequiresOnline>
+                    <Preloader type="ring" />
+                </RequiresOnline>
+            )}
+        </div>
+    );
 }
diff --git a/src/components/common/messaging/bars/FilePreview.tsx b/src/components/common/messaging/bars/FilePreview.tsx
index 92137d9..e8cea2f 100644
--- a/src/components/common/messaging/bars/FilePreview.tsx
+++ b/src/components/common/messaging/bars/FilePreview.tsx
@@ -9,225 +9,225 @@ import { determineFileSize } from "../../../../lib/fileSize";
 import { CAN_UPLOAD_AT_ONCE, UploadState } from "../MessageBox";
 
 interface Props {
-	state: UploadState;
-	addFile: () => void;
-	removeFile: (index: number) => void;
+    state: UploadState;
+    addFile: () => void;
+    removeFile: (index: number) => void;
 }
 
 const Container = styled.div`
-	gap: 4px;
-	padding: 8px;
-	display: flex;
-	user-select: none;
-	flex-direction: column;
-	background: var(--message-box);
+    gap: 4px;
+    padding: 8px;
+    display: flex;
+    user-select: none;
+    flex-direction: column;
+    background: var(--message-box);
 `;
 
 const Carousel = styled.div`
-	gap: 8px;
-	display: flex;
-	overflow-x: scroll;
-	flex-direction: row;
+    gap: 8px;
+    display: flex;
+    overflow-x: scroll;
+    flex-direction: row;
 `;
 
 const Entry = styled.div`
-	display: flex;
-	flex-direction: column;
-
-	&.fade {
-		opacity: 0.4;
-	}
-
-	span.fn {
-		margin: auto;
-		font-size: 0.8em;
-		overflow: hidden;
-		max-width: 180px;
-		text-align: center;
-		white-space: nowrap;
-		text-overflow: ellipsis;
-		color: var(--secondary-foreground);
-	}
-
-	span.size {
-		font-size: 0.6em;
-		color: var(--tertiary-foreground);
-		text-align: center;
-	}
+    display: flex;
+    flex-direction: column;
+
+    &.fade {
+        opacity: 0.4;
+    }
+
+    span.fn {
+        margin: auto;
+        font-size: 0.8em;
+        overflow: hidden;
+        max-width: 180px;
+        text-align: center;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        color: var(--secondary-foreground);
+    }
+
+    span.size {
+        font-size: 0.6em;
+        color: var(--tertiary-foreground);
+        text-align: center;
+    }
 `;
 
 const Description = styled.div`
-	gap: 4px;
-	display: flex;
-	font-size: 0.9em;
-	align-items: center;
-	color: var(--secondary-foreground);
+    gap: 4px;
+    display: flex;
+    font-size: 0.9em;
+    align-items: center;
+    color: var(--secondary-foreground);
 `;
 
 const Divider = styled.div`
-	width: 4px;
-	height: 130px;
-	flex-shrink: 0;
-	border-radius: 4px;
-	background: var(--tertiary-background);
+    width: 4px;
+    height: 130px;
+    flex-shrink: 0;
+    border-radius: 4px;
+    background: var(--tertiary-background);
 `;
 
 const EmptyEntry = styled.div`
-	width: 100px;
-	height: 100px;
-	display: grid;
-	flex-shrink: 0;
-	cursor: pointer;
-	border-radius: 4px;
-	place-items: center;
-	background: var(--primary-background);
-	transition: 0.1s ease background-color;
-
-	&:hover {
-		background: var(--secondary-background);
-	}
+    width: 100px;
+    height: 100px;
+    display: grid;
+    flex-shrink: 0;
+    cursor: pointer;
+    border-radius: 4px;
+    place-items: center;
+    background: var(--primary-background);
+    transition: 0.1s ease background-color;
+
+    &:hover {
+        background: var(--secondary-background);
+    }
 `;
 
 const PreviewBox = styled.div`
-	display: grid;
-	grid-template: "main" 100px / minmax(100px, 1fr);
-	justify-items: center;
-
-	background: var(--primary-background);
-
-	overflow: hidden;
-
-	cursor: pointer;
-	border-radius: 4px;
-
-	.icon,
-	.overlay {
-		grid-area: main;
-	}
-
-	.icon {
-		height: 100px;
-		width: 100%;
-		margin-bottom: 4px;
-		object-fit: contain;
-	}
-
-	.overlay {
-		display: grid;
-		align-items: center;
-		justify-content: center;
-
-		width: 100%;
-		height: 100%;
-
-		opacity: 0;
-		visibility: hidden;
-
-		transition: 0.1s ease opacity;
-	}
-
-	&:hover {
-		.overlay {
-			visibility: visible;
-			opacity: 1;
-			background-color: rgba(0, 0, 0, 0.8);
-		}
-	}
+    display: grid;
+    grid-template: "main" 100px / minmax(100px, 1fr);
+    justify-items: center;
+
+    background: var(--primary-background);
+
+    overflow: hidden;
+
+    cursor: pointer;
+    border-radius: 4px;
+
+    .icon,
+    .overlay {
+        grid-area: main;
+    }
+
+    .icon {
+        height: 100px;
+        width: 100%;
+        margin-bottom: 4px;
+        object-fit: contain;
+    }
+
+    .overlay {
+        display: grid;
+        align-items: center;
+        justify-content: center;
+
+        width: 100%;
+        height: 100%;
+
+        opacity: 0;
+        visibility: hidden;
+
+        transition: 0.1s ease opacity;
+    }
+
+    &:hover {
+        .overlay {
+            visibility: visible;
+            opacity: 1;
+            background-color: rgba(0, 0, 0, 0.8);
+        }
+    }
 `;
 
 function FileEntry({
-	file,
-	remove,
-	index,
+    file,
+    remove,
+    index,
 }: {
-	file: File;
-	remove?: () => void;
-	index: number;
+    file: File;
+    remove?: () => void;
+    index: number;
 }) {
-	if (!file.type.startsWith("image/"))
-		return (
-			<Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}>
-				<PreviewBox onClick={remove}>
-					<EmptyEntry className="icon">
-						<File size={36} />
-					</EmptyEntry>
-					<div class="overlay">
-						<XCircle size={36} />
-					</div>
-				</PreviewBox>
-				<span class="fn">{file.name}</span>
-				<span class="size">{determineFileSize(file.size)}</span>
-			</Entry>
-		);
-
-	const [url, setURL] = useState("");
-
-	useEffect(() => {
-		let url: string = URL.createObjectURL(file);
-		setURL(url);
-		return () => URL.revokeObjectURL(url);
-	}, [file]);
-
-	return (
-		<Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}>
-			<PreviewBox onClick={remove}>
-				<img class="icon" src={url} alt={file.name} />
-				<div class="overlay">
-					<XCircle size={36} />
-				</div>
-			</PreviewBox>
-			<span class="fn">{file.name}</span>
-			<span class="size">{determineFileSize(file.size)}</span>
-		</Entry>
-	);
+    if (!file.type.startsWith("image/"))
+        return (
+            <Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}>
+                <PreviewBox onClick={remove}>
+                    <EmptyEntry className="icon">
+                        <File size={36} />
+                    </EmptyEntry>
+                    <div class="overlay">
+                        <XCircle size={36} />
+                    </div>
+                </PreviewBox>
+                <span class="fn">{file.name}</span>
+                <span class="size">{determineFileSize(file.size)}</span>
+            </Entry>
+        );
+
+    const [url, setURL] = useState("");
+
+    useEffect(() => {
+        let url: string = URL.createObjectURL(file);
+        setURL(url);
+        return () => URL.revokeObjectURL(url);
+    }, [file]);
+
+    return (
+        <Entry className={index >= CAN_UPLOAD_AT_ONCE ? "fade" : ""}>
+            <PreviewBox onClick={remove}>
+                <img class="icon" src={url} alt={file.name} />
+                <div class="overlay">
+                    <XCircle size={36} />
+                </div>
+            </PreviewBox>
+            <span class="fn">{file.name}</span>
+            <span class="size">{determineFileSize(file.size)}</span>
+        </Entry>
+    );
 }
 
 export default function FilePreview({ state, addFile, removeFile }: Props) {
-	if (state.type === "none") return null;
-
-	return (
-		<Container>
-			<Carousel>
-				{state.files.map((file, index) => (
-					<>
-						{index === CAN_UPLOAD_AT_ONCE && <Divider />}
-						<FileEntry
-							index={index}
-							file={file}
-							key={file.name}
-							remove={
-								state.type === "attached"
-									? () => removeFile(index)
-									: undefined
-							}
-						/>
-					</>
-				))}
-				{state.type === "attached" && (
-					<EmptyEntry onClick={addFile}>
-						<Plus size={48} />
-					</EmptyEntry>
-				)}
-			</Carousel>
-			{state.type === "uploading" && (
-				<Description>
-					<Share size={24} />
-					<Text id="app.main.channel.uploading_file" /> (
-					{state.percent}%)
-				</Description>
-			)}
-			{state.type === "sending" && (
-				<Description>
-					<Share size={24} />
-					Sending...
-				</Description>
-			)}
-			{state.type === "failed" && (
-				<Description>
-					<X size={24} />
-					<Text id={`error.${state.error}`} />
-				</Description>
-			)}
-		</Container>
-	);
+    if (state.type === "none") return null;
+
+    return (
+        <Container>
+            <Carousel>
+                {state.files.map((file, index) => (
+                    <>
+                        {index === CAN_UPLOAD_AT_ONCE && <Divider />}
+                        <FileEntry
+                            index={index}
+                            file={file}
+                            key={file.name}
+                            remove={
+                                state.type === "attached"
+                                    ? () => removeFile(index)
+                                    : undefined
+                            }
+                        />
+                    </>
+                ))}
+                {state.type === "attached" && (
+                    <EmptyEntry onClick={addFile}>
+                        <Plus size={48} />
+                    </EmptyEntry>
+                )}
+            </Carousel>
+            {state.type === "uploading" && (
+                <Description>
+                    <Share size={24} />
+                    <Text id="app.main.channel.uploading_file" /> (
+                    {state.percent}%)
+                </Description>
+            )}
+            {state.type === "sending" && (
+                <Description>
+                    <Share size={24} />
+                    Sending...
+                </Description>
+            )}
+            {state.type === "failed" && (
+                <Description>
+                    <X size={24} />
+                    <Text id={`error.${state.error}`} />
+                </Description>
+            )}
+        </Container>
+    );
 }
diff --git a/src/components/common/messaging/bars/JumpToBottom.tsx b/src/components/common/messaging/bars/JumpToBottom.tsx
index 9accc90..0293fc1 100644
--- a/src/components/common/messaging/bars/JumpToBottom.tsx
+++ b/src/components/common/messaging/bars/JumpToBottom.tsx
@@ -4,61 +4,61 @@ import styled from "styled-components";
 import { Text } from "preact-i18n";
 
 import {
-	SingletonMessageRenderer,
-	useRenderState,
+    SingletonMessageRenderer,
+    useRenderState,
 } from "../../../../lib/renderer/Singleton";
 
 const Bar = styled.div`
-	z-index: 10;
-	position: relative;
+    z-index: 10;
+    position: relative;
 
-	> div {
-		top: -26px;
-		width: 100%;
-		position: absolute;
-		border-radius: 4px 4px 0 0;
-		display: flex;
-		cursor: pointer;
-		font-size: 13px;
-		padding: 4px 8px;
-		user-select: none;
-		color: var(--secondary-foreground);
-		background: var(--secondary-background);
-		justify-content: space-between;
-		transition: color ease-in-out 0.08s;
+    > div {
+        top: -26px;
+        width: 100%;
+        position: absolute;
+        border-radius: 4px 4px 0 0;
+        display: flex;
+        cursor: pointer;
+        font-size: 13px;
+        padding: 4px 8px;
+        user-select: none;
+        color: var(--secondary-foreground);
+        background: var(--secondary-background);
+        justify-content: space-between;
+        transition: color ease-in-out 0.08s;
 
-		> div {
-			display: flex;
-			align-items: center;
-			gap: 6px;
-		}
+        > div {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+        }
 
-		&:hover {
-			color: var(--primary-text);
-		}
+        &:hover {
+            color: var(--primary-text);
+        }
 
-		&:active {
-			transform: translateY(1px);
-		}
-	}
+        &:active {
+            transform: translateY(1px);
+        }
+    }
 `;
 
 export default function JumpToBottom({ id }: { id: string }) {
-	const view = useRenderState(id);
-	if (!view || view.type !== "RENDER" || view.atBottom) return null;
+    const view = useRenderState(id);
+    if (!view || view.type !== "RENDER" || view.atBottom) return null;
 
-	return (
-		<Bar>
-			<div
-				onClick={() => SingletonMessageRenderer.jumpToBottom(id, true)}>
-				<div>
-					<Text id="app.main.channel.misc.viewing_old" />
-				</div>
-				<div>
-					<Text id="app.main.channel.misc.jump_present" />{" "}
-					<DownArrow size={18} />
-				</div>
-			</div>
-		</Bar>
-	);
+    return (
+        <Bar>
+            <div
+                onClick={() => SingletonMessageRenderer.jumpToBottom(id, true)}>
+                <div>
+                    <Text id="app.main.channel.misc.viewing_old" />
+                </div>
+                <div>
+                    <Text id="app.main.channel.misc.jump_present" />{" "}
+                    <DownArrow size={18} />
+                </div>
+            </div>
+        </Bar>
+    );
 }
diff --git a/src/components/common/messaging/bars/ReplyBar.tsx b/src/components/common/messaging/bars/ReplyBar.tsx
index 5c4dbda..4199c13 100644
--- a/src/components/common/messaging/bars/ReplyBar.tsx
+++ b/src/components/common/messaging/bars/ReplyBar.tsx
@@ -1,8 +1,8 @@
 import {
-	At,
-	Reply as ReplyIcon,
-	File,
-	XCircle,
+    At,
+    Reply as ReplyIcon,
+    File,
+    XCircle,
 } from "@styled-icons/boxicons-regular";
 import styled from "styled-components";
 
@@ -23,119 +23,119 @@ import UserShort from "../../user/UserShort";
 import { ReplyBase } from "../attachments/MessageReply";
 
 interface Props {
-	channel: string;
-	replies: Reply[];
-	setReplies: StateUpdater<Reply[]>;
+    channel: string;
+    replies: Reply[];
+    setReplies: StateUpdater<Reply[]>;
 }
 
 const Base = styled.div`
-	display: flex;
-	padding: 0 22px;
-	user-select: none;
-	align-items: center;
-	background: var(--message-box);
-
-	div {
-		flex-grow: 1;
-	}
-
-	.actions {
-		gap: 12px;
-		display: flex;
-	}
-
-	.toggle {
-		gap: 4px;
-		display: flex;
-		font-size: 0.7em;
-		align-items: center;
-	}
+    display: flex;
+    padding: 0 22px;
+    user-select: none;
+    align-items: center;
+    background: var(--message-box);
+
+    div {
+        flex-grow: 1;
+    }
+
+    .actions {
+        gap: 12px;
+        display: flex;
+    }
+
+    .toggle {
+        gap: 4px;
+        display: flex;
+        font-size: 0.7em;
+        align-items: center;
+    }
 `;
 
 // ! FIXME: Move to global config
 const MAX_REPLIES = 5;
 export default function ReplyBar({ channel, replies, setReplies }: Props) {
-	useEffect(() => {
-		return internalSubscribe(
-			"ReplyBar",
-			"add",
-			(id) =>
-				replies.length < MAX_REPLIES &&
-				!replies.find((x) => x.id === id) &&
-				setReplies([...replies, { id, mention: false }]),
-		);
-	}, [replies]);
-
-	const view = useRenderState(channel);
-	if (view?.type !== "RENDER") return null;
-
-	const ids = replies.map((x) => x.id);
-	const messages = view.messages.filter((x) => ids.includes(x._id));
-	const users = useUsers(messages.map((x) => x.author));
-
-	return (
-		<div>
-			{replies.map((reply, index) => {
-				let message = messages.find((x) => reply.id === x._id);
-				// ! FIXME: better solution would be to
-				// ! have a hook for resolving messages from
-				// ! render state along with relevant users
-				// -> which then fetches any unknown messages
-				if (!message)
-					return (
-						<span>
-							<Text id="app.main.channel.misc.failed_load" />
-						</span>
-					);
-
-				let user = users.find((x) => message!.author === x?._id);
-				if (!user) return;
-
-				return (
-					<Base key={reply.id}>
-						<ReplyBase preview>
-							<ReplyIcon size={22} />
-							<UserShort user={user} size={16} />
-							{message.attachments &&
-								message.attachments.length > 0 && (
-									<File size={16} />
-								)}
-							<Markdown
-								disallowBigEmoji
-								content={(message.content as string).replace(
-									/\n/g,
-									" ",
-								)}
-							/>
-						</ReplyBase>
-						<span class="actions">
-							<IconButton
-								onClick={() =>
-									setReplies(
-										replies.map((_, i) =>
-											i === index
-												? { ..._, mention: !_.mention }
-												: _,
-										),
-									)
-								}>
-								<span class="toggle">
-									<At size={16} />{" "}
-									{reply.mention ? "ON" : "OFF"}
-								</span>
-							</IconButton>
-							<IconButton
-								onClick={() =>
-									setReplies(
-										replies.filter((_, i) => i !== index),
-									)
-								}>
-								<XCircle size={16} />
-							</IconButton>
-						</span>
-					</Base>
-				);
-			})}
-		</div>
-	);
+    useEffect(() => {
+        return internalSubscribe(
+            "ReplyBar",
+            "add",
+            (id) =>
+                replies.length < MAX_REPLIES &&
+                !replies.find((x) => x.id === id) &&
+                setReplies([...replies, { id, mention: false }]),
+        );
+    }, [replies]);
+
+    const view = useRenderState(channel);
+    if (view?.type !== "RENDER") return null;
+
+    const ids = replies.map((x) => x.id);
+    const messages = view.messages.filter((x) => ids.includes(x._id));
+    const users = useUsers(messages.map((x) => x.author));
+
+    return (
+        <div>
+            {replies.map((reply, index) => {
+                let message = messages.find((x) => reply.id === x._id);
+                // ! FIXME: better solution would be to
+                // ! have a hook for resolving messages from
+                // ! render state along with relevant users
+                // -> which then fetches any unknown messages
+                if (!message)
+                    return (
+                        <span>
+                            <Text id="app.main.channel.misc.failed_load" />
+                        </span>
+                    );
+
+                let user = users.find((x) => message!.author === x?._id);
+                if (!user) return;
+
+                return (
+                    <Base key={reply.id}>
+                        <ReplyBase preview>
+                            <ReplyIcon size={22} />
+                            <UserShort user={user} size={16} />
+                            {message.attachments &&
+                                message.attachments.length > 0 && (
+                                    <File size={16} />
+                                )}
+                            <Markdown
+                                disallowBigEmoji
+                                content={(message.content as string).replace(
+                                    /\n/g,
+                                    " ",
+                                )}
+                            />
+                        </ReplyBase>
+                        <span class="actions">
+                            <IconButton
+                                onClick={() =>
+                                    setReplies(
+                                        replies.map((_, i) =>
+                                            i === index
+                                                ? { ..._, mention: !_.mention }
+                                                : _,
+                                        ),
+                                    )
+                                }>
+                                <span class="toggle">
+                                    <At size={16} />{" "}
+                                    {reply.mention ? "ON" : "OFF"}
+                                </span>
+                            </IconButton>
+                            <IconButton
+                                onClick={() =>
+                                    setReplies(
+                                        replies.filter((_, i) => i !== index),
+                                    )
+                                }>
+                                <XCircle size={16} />
+                            </IconButton>
+                        </span>
+                    </Base>
+                );
+            })}
+        </div>
+    );
 }
diff --git a/src/components/common/messaging/bars/TypingIndicator.tsx b/src/components/common/messaging/bars/TypingIndicator.tsx
index a00e2ff..e58a85b 100644
--- a/src/components/common/messaging/bars/TypingIndicator.tsx
+++ b/src/components/common/messaging/bars/TypingIndicator.tsx
@@ -11,111 +11,111 @@ import { AppContext } from "../../../../context/revoltjs/RevoltClient";
 import { useUsers } from "../../../../context/revoltjs/hooks";
 
 interface Props {
-	typing?: TypingUser[];
+    typing?: TypingUser[];
 }
 
 const Base = styled.div`
-	position: relative;
-
-	> div {
-		height: 24px;
-		margin-top: -24px;
-		position: absolute;
-
-		gap: 8px;
-		display: flex;
-		padding: 0 10px;
-		user-select: none;
-		align-items: center;
-		flex-direction: row;
-		width: calc(100% - 3px);
-		color: var(--secondary-foreground);
-		background: var(--secondary-background);
-	}
-
-	.avatars {
-		display: flex;
-
-		img {
-			width: 16px;
-			height: 16px;
-			object-fit: cover;
-			border-radius: 50%;
-
-			&:not(:first-child) {
-				margin-left: -4px;
-			}
-		}
-	}
-
-	.usernames {
-		min-width: 0;
-		font-size: 13px;
-		overflow: hidden;
-		white-space: nowrap;
-		text-overflow: ellipsis;
-	}
+    position: relative;
+
+    > div {
+        height: 24px;
+        margin-top: -24px;
+        position: absolute;
+
+        gap: 8px;
+        display: flex;
+        padding: 0 10px;
+        user-select: none;
+        align-items: center;
+        flex-direction: row;
+        width: calc(100% - 3px);
+        color: var(--secondary-foreground);
+        background: var(--secondary-background);
+    }
+
+    .avatars {
+        display: flex;
+
+        img {
+            width: 16px;
+            height: 16px;
+            object-fit: cover;
+            border-radius: 50%;
+
+            &:not(:first-child) {
+                margin-left: -4px;
+            }
+        }
+    }
+
+    .usernames {
+        min-width: 0;
+        font-size: 13px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+    }
 `;
 
 export function TypingIndicator({ typing }: Props) {
-	if (typing && typing.length > 0) {
-		const client = useContext(AppContext);
-		const users = useUsers(typing.map((x) => x.id)).filter(
-			(x) => typeof x !== "undefined",
-		) as User[];
-
-		users.sort((a, b) =>
-			a._id.toUpperCase().localeCompare(b._id.toUpperCase()),
-		);
-
-		let text;
-		if (users.length >= 5) {
-			text = <Text id="app.main.channel.typing.several" />;
-		} else if (users.length > 1) {
-			const usersCopy = [...users];
-			text = (
-				<Text
-					id="app.main.channel.typing.multiple"
-					fields={{
-						user: usersCopy.pop()?.username,
-						userlist: usersCopy.map((x) => x.username).join(", "),
-					}}
-				/>
-			);
-		} else {
-			text = (
-				<Text
-					id="app.main.channel.typing.single"
-					fields={{ user: users[0].username }}
-				/>
-			);
-		}
-
-		return (
-			<Base>
-				<div>
-					<div className="avatars">
-						{users.map((user) => (
-							<img
-								src={client.users.getAvatarURL(
-									user._id,
-									{ max_side: 256 },
-									true,
-								)}
-							/>
-						))}
-					</div>
-					<div className="usernames">{text}</div>
-				</div>
-			</Base>
-		);
-	}
-
-	return null;
+    if (typing && typing.length > 0) {
+        const client = useContext(AppContext);
+        const users = useUsers(typing.map((x) => x.id)).filter(
+            (x) => typeof x !== "undefined",
+        ) as User[];
+
+        users.sort((a, b) =>
+            a._id.toUpperCase().localeCompare(b._id.toUpperCase()),
+        );
+
+        let text;
+        if (users.length >= 5) {
+            text = <Text id="app.main.channel.typing.several" />;
+        } else if (users.length > 1) {
+            const usersCopy = [...users];
+            text = (
+                <Text
+                    id="app.main.channel.typing.multiple"
+                    fields={{
+                        user: usersCopy.pop()?.username,
+                        userlist: usersCopy.map((x) => x.username).join(", "),
+                    }}
+                />
+            );
+        } else {
+            text = (
+                <Text
+                    id="app.main.channel.typing.single"
+                    fields={{ user: users[0].username }}
+                />
+            );
+        }
+
+        return (
+            <Base>
+                <div>
+                    <div className="avatars">
+                        {users.map((user) => (
+                            <img
+                                src={client.users.getAvatarURL(
+                                    user._id,
+                                    { max_side: 256 },
+                                    true,
+                                )}
+                            />
+                        ))}
+                    </div>
+                    <div className="usernames">{text}</div>
+                </div>
+            </Base>
+        );
+    }
+
+    return null;
 }
 
 export default connectState<{ id: string }>(TypingIndicator, (state, props) => {
-	return {
-		typing: state.typing && state.typing[props.id],
-	};
+    return {
+        typing: state.typing && state.typing[props.id],
+    };
 });
diff --git a/src/components/common/messaging/embed/Embed.tsx b/src/components/common/messaging/embed/Embed.tsx
index fa7a09f..7284b7c 100644
--- a/src/components/common/messaging/embed/Embed.tsx
+++ b/src/components/common/messaging/embed/Embed.tsx
@@ -10,7 +10,7 @@ import { MessageAreaWidthContext } from "../../../../pages/channels/messaging/Me
 import EmbedMedia from "./EmbedMedia";
 
 interface Props {
-	embed: EmbedRJS;
+    embed: EmbedRJS;
 }
 
 const MAX_EMBED_WIDTH = 480;
@@ -19,149 +19,149 @@ const CONTAINER_PADDING = 24;
 const MAX_PREVIEW_SIZE = 150;
 
 export default function Embed({ embed }: Props) {
-	// ! FIXME: temp code
-	// ! add proxy function to client
-	function proxyImage(url: string) {
-		return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
-	}
-
-	const { openScreen } = useIntermediate();
-	const maxWidth = Math.min(
-		useContext(MessageAreaWidthContext) - CONTAINER_PADDING,
-		MAX_EMBED_WIDTH,
-	);
-
-	function calculateSize(
-		w: number,
-		h: number,
-	): { width: number; height: number } {
-		let limitingWidth = Math.min(maxWidth, w);
-
-		let limitingHeight = Math.min(MAX_EMBED_HEIGHT, h);
-
-		// Calculate smallest possible WxH.
-		let width = Math.min(limitingWidth, limitingHeight * (w / h));
-
-		let height = Math.min(limitingHeight, limitingWidth * (h / w));
-
-		return { width, height };
-	}
-
-	switch (embed.type) {
-		case "Website": {
-			// Determine special embed size.
-			let mw, mh;
-			let largeMedia =
-				(embed.special && embed.special.type !== "None") ||
-				embed.image?.size === "Large";
-			switch (embed.special?.type) {
-				case "YouTube":
-				case "Bandcamp": {
-					mw = embed.video?.width ?? 1280;
-					mh = embed.video?.height ?? 720;
-					break;
-				}
-				case "Twitch": {
-					mw = 1280;
-					mh = 720;
-					break;
-				}
-				default: {
-					if (embed.image?.size === "Preview") {
-						mw = MAX_EMBED_WIDTH;
-						mh = Math.min(
-							embed.image.height ?? 0,
-							MAX_PREVIEW_SIZE,
-						);
-					} else {
-						mw = embed.image?.width ?? MAX_EMBED_WIDTH;
-						mh = embed.image?.height ?? 0;
-					}
-				}
-			}
-
-			let { width, height } = calculateSize(mw, mh);
-			return (
-				<div
-					className={classNames(styles.embed, styles.website)}
-					style={{
-						borderInlineStartColor:
-							embed.color ?? "var(--tertiary-background)",
-						width: width + CONTAINER_PADDING,
-					}}>
-					<div>
-						{embed.site_name && (
-							<div className={styles.siteinfo}>
-								{embed.icon_url && (
-									<img
-										className={styles.favicon}
-										src={proxyImage(embed.icon_url)}
-										draggable={false}
-										onError={(e) =>
-											(e.currentTarget.style.display =
-												"none")
-										}
-									/>
-								)}
-								<div className={styles.site}>
-									{embed.site_name}{" "}
-								</div>
-							</div>
-						)}
-
-						{/*<span><a href={embed.url} target={"_blank"} className={styles.author}>Author</a></span>*/}
-						{embed.title && (
-							<span>
-								<a
-									href={embed.url}
-									target={"_blank"}
-									className={styles.title}>
-									{embed.title}
-								</a>
-							</span>
-						)}
-						{embed.description && (
-							<div className={styles.description}>
-								{embed.description}
-							</div>
-						)}
-
-						{largeMedia && (
-							<EmbedMedia embed={embed} height={height} />
-						)}
-					</div>
-					{!largeMedia && (
-						<div>
-							<EmbedMedia
-								embed={embed}
-								width={
-									height *
-									((embed.image?.width ?? 0) /
-										(embed.image?.height ?? 0))
-								}
-								height={height}
-							/>
-						</div>
-					)}
-				</div>
-			);
-		}
-		case "Image": {
-			return (
-				<img
-					className={classNames(styles.embed, styles.image)}
-					style={calculateSize(embed.width, embed.height)}
-					src={proxyImage(embed.url)}
-					type="text/html"
-					frameBorder="0"
-					onClick={() => openScreen({ id: "image_viewer", embed })}
-					onMouseDown={(ev) =>
-						ev.button === 1 && window.open(embed.url, "_blank")
-					}
-				/>
-			);
-		}
-		default:
-			return null;
-	}
+    // ! FIXME: temp code
+    // ! add proxy function to client
+    function proxyImage(url: string) {
+        return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
+    }
+
+    const { openScreen } = useIntermediate();
+    const maxWidth = Math.min(
+        useContext(MessageAreaWidthContext) - CONTAINER_PADDING,
+        MAX_EMBED_WIDTH,
+    );
+
+    function calculateSize(
+        w: number,
+        h: number,
+    ): { width: number; height: number } {
+        let limitingWidth = Math.min(maxWidth, w);
+
+        let limitingHeight = Math.min(MAX_EMBED_HEIGHT, h);
+
+        // Calculate smallest possible WxH.
+        let width = Math.min(limitingWidth, limitingHeight * (w / h));
+
+        let height = Math.min(limitingHeight, limitingWidth * (h / w));
+
+        return { width, height };
+    }
+
+    switch (embed.type) {
+        case "Website": {
+            // Determine special embed size.
+            let mw, mh;
+            let largeMedia =
+                (embed.special && embed.special.type !== "None") ||
+                embed.image?.size === "Large";
+            switch (embed.special?.type) {
+                case "YouTube":
+                case "Bandcamp": {
+                    mw = embed.video?.width ?? 1280;
+                    mh = embed.video?.height ?? 720;
+                    break;
+                }
+                case "Twitch": {
+                    mw = 1280;
+                    mh = 720;
+                    break;
+                }
+                default: {
+                    if (embed.image?.size === "Preview") {
+                        mw = MAX_EMBED_WIDTH;
+                        mh = Math.min(
+                            embed.image.height ?? 0,
+                            MAX_PREVIEW_SIZE,
+                        );
+                    } else {
+                        mw = embed.image?.width ?? MAX_EMBED_WIDTH;
+                        mh = embed.image?.height ?? 0;
+                    }
+                }
+            }
+
+            let { width, height } = calculateSize(mw, mh);
+            return (
+                <div
+                    className={classNames(styles.embed, styles.website)}
+                    style={{
+                        borderInlineStartColor:
+                            embed.color ?? "var(--tertiary-background)",
+                        width: width + CONTAINER_PADDING,
+                    }}>
+                    <div>
+                        {embed.site_name && (
+                            <div className={styles.siteinfo}>
+                                {embed.icon_url && (
+                                    <img
+                                        className={styles.favicon}
+                                        src={proxyImage(embed.icon_url)}
+                                        draggable={false}
+                                        onError={(e) =>
+                                            (e.currentTarget.style.display =
+                                                "none")
+                                        }
+                                    />
+                                )}
+                                <div className={styles.site}>
+                                    {embed.site_name}{" "}
+                                </div>
+                            </div>
+                        )}
+
+                        {/*<span><a href={embed.url} target={"_blank"} className={styles.author}>Author</a></span>*/}
+                        {embed.title && (
+                            <span>
+                                <a
+                                    href={embed.url}
+                                    target={"_blank"}
+                                    className={styles.title}>
+                                    {embed.title}
+                                </a>
+                            </span>
+                        )}
+                        {embed.description && (
+                            <div className={styles.description}>
+                                {embed.description}
+                            </div>
+                        )}
+
+                        {largeMedia && (
+                            <EmbedMedia embed={embed} height={height} />
+                        )}
+                    </div>
+                    {!largeMedia && (
+                        <div>
+                            <EmbedMedia
+                                embed={embed}
+                                width={
+                                    height *
+                                    ((embed.image?.width ?? 0) /
+                                        (embed.image?.height ?? 0))
+                                }
+                                height={height}
+                            />
+                        </div>
+                    )}
+                </div>
+            );
+        }
+        case "Image": {
+            return (
+                <img
+                    className={classNames(styles.embed, styles.image)}
+                    style={calculateSize(embed.width, embed.height)}
+                    src={proxyImage(embed.url)}
+                    type="text/html"
+                    frameBorder="0"
+                    onClick={() => openScreen({ id: "image_viewer", embed })}
+                    onMouseDown={(ev) =>
+                        ev.button === 1 && window.open(embed.url, "_blank")
+                    }
+                />
+            );
+        }
+        default:
+            return null;
+    }
 }
diff --git a/src/components/common/messaging/embed/EmbedMedia.tsx b/src/components/common/messaging/embed/EmbedMedia.tsx
index b97922d..73ab3e4 100644
--- a/src/components/common/messaging/embed/EmbedMedia.tsx
+++ b/src/components/common/messaging/embed/EmbedMedia.tsx
@@ -5,96 +5,96 @@ import styles from "./Embed.module.scss";
 import { useIntermediate } from "../../../../context/intermediate/Intermediate";
 
 interface Props {
-	embed: Embed;
-	width?: number;
-	height: number;
+    embed: Embed;
+    width?: number;
+    height: number;
 }
 
 export default function EmbedMedia({ embed, width, height }: Props) {
-	// ! FIXME: temp code
-	// ! add proxy function to client
-	function proxyImage(url: string) {
-		return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
-	}
+    // ! FIXME: temp code
+    // ! add proxy function to client
+    function proxyImage(url: string) {
+        return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
+    }
 
-	if (embed.type !== "Website") return null;
-	const { openScreen } = useIntermediate();
+    if (embed.type !== "Website") return null;
+    const { openScreen } = useIntermediate();
 
-	switch (embed.special?.type) {
-		case "YouTube":
-			return (
-				<iframe
-					src={`https://www.youtube-nocookie.com/embed/${embed.special.id}?modestbranding=1`}
-					allowFullScreen
-					style={{ height }}
-				/>
-			);
-		case "Twitch":
-			return (
-				<iframe
-					src={`https://player.twitch.tv/?${embed.special.content_type.toLowerCase()}=${
-						embed.special.id
-					}&parent=${window.location.hostname}&autoplay=false`}
-					frameBorder="0"
-					allowFullScreen
-					scrolling="no"
-					style={{ height }}
-				/>
-			);
-		case "Spotify":
-			return (
-				<iframe
-					src={`https://open.spotify.com/embed/${embed.special.content_type}/${embed.special.id}`}
-					frameBorder="0"
-					allowFullScreen
-					allowTransparency
-					style={{ height }}
-				/>
-			);
-		case "Soundcloud":
-			return (
-				<iframe
-					src={`https://w.soundcloud.com/player/?url=${encodeURIComponent(
-						embed.url!,
-					)}&color=%23FF7F50&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true`}
-					frameBorder="0"
-					scrolling="no"
-					style={{ height }}
-				/>
-			);
-		case "Bandcamp": {
-			return (
-				<iframe
-					src={`https://bandcamp.com/EmbeddedPlayer/${embed.special.content_type.toLowerCase()}=${
-						embed.special.id
-					}/size=large/bgcol=181a1b/linkcol=056cc4/tracklist=false/transparent=true/`}
-					seamless
-					style={{ height }}
-				/>
-			);
-		}
-		default: {
-			if (embed.image) {
-				let url = embed.image.url;
-				return (
-					<img
-						className={styles.image}
-						src={proxyImage(url)}
-						style={{ width, height }}
-						onClick={() =>
-							openScreen({
-								id: "image_viewer",
-								embed: embed.image,
-							})
-						}
-						onMouseDown={(ev) =>
-							ev.button === 1 && window.open(url, "_blank")
-						}
-					/>
-				);
-			}
-		}
-	}
+    switch (embed.special?.type) {
+        case "YouTube":
+            return (
+                <iframe
+                    src={`https://www.youtube-nocookie.com/embed/${embed.special.id}?modestbranding=1`}
+                    allowFullScreen
+                    style={{ height }}
+                />
+            );
+        case "Twitch":
+            return (
+                <iframe
+                    src={`https://player.twitch.tv/?${embed.special.content_type.toLowerCase()}=${
+                        embed.special.id
+                    }&parent=${window.location.hostname}&autoplay=false`}
+                    frameBorder="0"
+                    allowFullScreen
+                    scrolling="no"
+                    style={{ height }}
+                />
+            );
+        case "Spotify":
+            return (
+                <iframe
+                    src={`https://open.spotify.com/embed/${embed.special.content_type}/${embed.special.id}`}
+                    frameBorder="0"
+                    allowFullScreen
+                    allowTransparency
+                    style={{ height }}
+                />
+            );
+        case "Soundcloud":
+            return (
+                <iframe
+                    src={`https://w.soundcloud.com/player/?url=${encodeURIComponent(
+                        embed.url!,
+                    )}&color=%23FF7F50&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true`}
+                    frameBorder="0"
+                    scrolling="no"
+                    style={{ height }}
+                />
+            );
+        case "Bandcamp": {
+            return (
+                <iframe
+                    src={`https://bandcamp.com/EmbeddedPlayer/${embed.special.content_type.toLowerCase()}=${
+                        embed.special.id
+                    }/size=large/bgcol=181a1b/linkcol=056cc4/tracklist=false/transparent=true/`}
+                    seamless
+                    style={{ height }}
+                />
+            );
+        }
+        default: {
+            if (embed.image) {
+                let url = embed.image.url;
+                return (
+                    <img
+                        className={styles.image}
+                        src={proxyImage(url)}
+                        style={{ width, height }}
+                        onClick={() =>
+                            openScreen({
+                                id: "image_viewer",
+                                embed: embed.image,
+                            })
+                        }
+                        onMouseDown={(ev) =>
+                            ev.button === 1 && window.open(url, "_blank")
+                        }
+                    />
+                );
+            }
+        }
+    }
 
-	return null;
+    return null;
 }
diff --git a/src/components/common/messaging/embed/EmbedMediaActions.tsx b/src/components/common/messaging/embed/EmbedMediaActions.tsx
index 1f7dd4c..bc8f41a 100644
--- a/src/components/common/messaging/embed/EmbedMediaActions.tsx
+++ b/src/components/common/messaging/embed/EmbedMediaActions.tsx
@@ -6,25 +6,25 @@ import styles from "./Embed.module.scss";
 import IconButton from "../../../ui/IconButton";
 
 interface Props {
-	embed: EmbedImage;
+    embed: EmbedImage;
 }
 
 export default function EmbedMediaActions({ embed }: Props) {
-	const filename = embed.url.split("/").pop();
+    const filename = embed.url.split("/").pop();
 
-	return (
-		<div className={styles.actions}>
-			<div className={styles.info}>
-				<span className={styles.filename}>{filename}</span>
-				<span className={styles.filesize}>
-					{embed.width + "x" + embed.height}
-				</span>
-			</div>
-			<a href={embed.url} target="_blank">
-				<IconButton>
-					<LinkExternal size={24} />
-				</IconButton>
-			</a>
-		</div>
-	);
+    return (
+        <div className={styles.actions}>
+            <div className={styles.info}>
+                <span className={styles.filename}>{filename}</span>
+                <span className={styles.filesize}>
+                    {embed.width + "x" + embed.height}
+                </span>
+            </div>
+            <a href={embed.url} target="_blank">
+                <IconButton>
+                    <LinkExternal size={24} />
+                </IconButton>
+            </a>
+        </div>
+    );
 }
diff --git a/src/components/common/user/UserCheckbox.tsx b/src/components/common/user/UserCheckbox.tsx
index 7e204dd..c125afd 100644
--- a/src/components/common/user/UserCheckbox.tsx
+++ b/src/components/common/user/UserCheckbox.tsx
@@ -7,10 +7,10 @@ import UserIcon from "./UserIcon";
 type UserProps = Omit<CheckboxProps, "children"> & { user: User };
 
 export default function UserCheckbox({ user, ...props }: UserProps) {
-	return (
-		<Checkbox {...props}>
-			<UserIcon target={user} size={32} />
-			{user.username}
-		</Checkbox>
-	);
+    return (
+        <Checkbox {...props}>
+            <UserIcon target={user} size={32} />
+            {user.username}
+        </Checkbox>
+    );
 }
diff --git a/src/components/common/user/UserHeader.tsx b/src/components/common/user/UserHeader.tsx
index 9783b3a..cd63f6c 100644
--- a/src/components/common/user/UserHeader.tsx
+++ b/src/components/common/user/UserHeader.tsx
@@ -19,66 +19,66 @@ import UserIcon from "./UserIcon";
 import UserStatus from "./UserStatus";
 
 const HeaderBase = styled.div`
-	gap: 0;
-	flex-grow: 1;
-	min-width: 0;
-	display: flex;
-	flex-direction: column;
+    gap: 0;
+    flex-grow: 1;
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
 
-	* {
-		min-width: 0;
-		overflow: hidden;
-		white-space: nowrap;
-		text-overflow: ellipsis;
-	}
+    * {
+        min-width: 0;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+    }
 
-	.username {
-		cursor: pointer;
-		font-size: 16px;
-		font-weight: 600;
-	}
+    .username {
+        cursor: pointer;
+        font-size: 16px;
+        font-weight: 600;
+    }
 
-	.status {
-		cursor: pointer;
-		font-size: 12px;
-		margin-top: -2px;
-	}
+    .status {
+        cursor: pointer;
+        font-size: 12px;
+        margin-top: -2px;
+    }
 `;
 
 interface Props {
-	user: User;
+    user: User;
 }
 
 export default function UserHeader({ user }: Props) {
-	const { writeClipboard } = useIntermediate();
+    const { writeClipboard } = useIntermediate();
 
-	return (
-		<Header borders placement="secondary">
-			<HeaderBase>
-				<Localizer>
-					<Tooltip content={<Text id="app.special.copy_username" />}>
-						<span
-							className="username"
-							onClick={() => writeClipboard(user.username)}>
-							@{user.username}
-						</span>
-					</Tooltip>
-				</Localizer>
-				<span
-					className="status"
-					onClick={() => openContextMenu("Status")}>
-					<UserStatus user={user} />
-				</span>
-			</HeaderBase>
-			{!isTouchscreenDevice && (
-				<div className="actions">
-					<Link to="/settings">
-						<IconButton>
-							<Cog size={24} />
-						</IconButton>
-					</Link>
-				</div>
-			)}
-		</Header>
-	);
+    return (
+        <Header borders placement="secondary">
+            <HeaderBase>
+                <Localizer>
+                    <Tooltip content={<Text id="app.special.copy_username" />}>
+                        <span
+                            className="username"
+                            onClick={() => writeClipboard(user.username)}>
+                            @{user.username}
+                        </span>
+                    </Tooltip>
+                </Localizer>
+                <span
+                    className="status"
+                    onClick={() => openContextMenu("Status")}>
+                    <UserStatus user={user} />
+                </span>
+            </HeaderBase>
+            {!isTouchscreenDevice && (
+                <div className="actions">
+                    <Link to="/settings">
+                        <IconButton>
+                            <Cog size={24} />
+                        </IconButton>
+                    </Link>
+                </div>
+            )}
+        </Header>
+    );
 }
diff --git a/src/components/common/user/UserIcon.tsx b/src/components/common/user/UserIcon.tsx
index d1c2fb9..e5cd3dc 100644
--- a/src/components/common/user/UserIcon.tsx
+++ b/src/components/common/user/UserIcon.tsx
@@ -13,92 +13,92 @@ import fallback from "../assets/user.png";
 
 type VoiceStatus = "muted";
 interface Props extends IconBaseProps<User> {
-	mask?: string;
-	status?: boolean;
-	voice?: VoiceStatus;
+    mask?: string;
+    status?: boolean;
+    voice?: VoiceStatus;
 }
 
 export function useStatusColour(user?: User) {
-	const theme = useContext(ThemeContext);
+    const theme = useContext(ThemeContext);
 
-	return user?.online && user?.status?.presence !== Users.Presence.Invisible
-		? user?.status?.presence === Users.Presence.Idle
-			? theme["status-away"]
-			: user?.status?.presence === Users.Presence.Busy
-			? theme["status-busy"]
-			: theme["status-online"]
-		: theme["status-invisible"];
+    return user?.online && user?.status?.presence !== Users.Presence.Invisible
+        ? user?.status?.presence === Users.Presence.Idle
+            ? theme["status-away"]
+            : user?.status?.presence === Users.Presence.Busy
+            ? theme["status-busy"]
+            : theme["status-online"]
+        : theme["status-invisible"];
 }
 
 const VoiceIndicator = styled.div<{ status: VoiceStatus }>`
-	width: 10px;
-	height: 10px;
-	border-radius: 50%;
+    width: 10px;
+    height: 10px;
+    border-radius: 50%;
 
-	display: flex;
-	align-items: center;
-	justify-content: center;
+    display: flex;
+    align-items: center;
+    justify-content: center;
 
-	svg {
-		stroke: white;
-	}
+    svg {
+        stroke: white;
+    }
 
-	${(props) =>
-		props.status === "muted" &&
-		css`
-			background: var(--error);
-		`}
+    ${(props) =>
+        props.status === "muted" &&
+        css`
+            background: var(--error);
+        `}
 `;
 
 export default function UserIcon(
-	props: Props & Omit<JSX.SVGAttributes<SVGSVGElement>, keyof Props>,
+    props: Props & Omit<JSX.SVGAttributes<SVGSVGElement>, keyof Props>,
 ) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	const {
-		target,
-		attachment,
-		size,
-		voice,
-		status,
-		animate,
-		mask,
-		children,
-		as,
-		...svgProps
-	} = props;
-	const iconURL =
-		client.generateFileURL(
-			target?.avatar ?? attachment,
-			{ max_side: 256 },
-			animate,
-		) ?? (target ? client.users.getDefaultAvatarURL(target._id) : fallback);
+    const {
+        target,
+        attachment,
+        size,
+        voice,
+        status,
+        animate,
+        mask,
+        children,
+        as,
+        ...svgProps
+    } = props;
+    const iconURL =
+        client.generateFileURL(
+            target?.avatar ?? attachment,
+            { max_side: 256 },
+            animate,
+        ) ?? (target ? client.users.getDefaultAvatarURL(target._id) : fallback);
 
-	return (
-		<IconBase
-			{...svgProps}
-			width={size}
-			height={size}
-			aria-hidden="true"
-			viewBox="0 0 32 32">
-			<foreignObject
-				x="0"
-				y="0"
-				width="32"
-				height="32"
-				mask={mask ?? (status ? "url(#user)" : undefined)}>
-				{<img src={iconURL} draggable={false} />}
-			</foreignObject>
-			{props.status && (
-				<circle cx="27" cy="27" r="5" fill={useStatusColour(target)} />
-			)}
-			{props.voice && (
-				<foreignObject x="22" y="22" width="10" height="10">
-					<VoiceIndicator status={props.voice}>
-						{props.voice === "muted" && <MicrophoneOff size={6} />}
-					</VoiceIndicator>
-				</foreignObject>
-			)}
-		</IconBase>
-	);
+    return (
+        <IconBase
+            {...svgProps}
+            width={size}
+            height={size}
+            aria-hidden="true"
+            viewBox="0 0 32 32">
+            <foreignObject
+                x="0"
+                y="0"
+                width="32"
+                height="32"
+                mask={mask ?? (status ? "url(#user)" : undefined)}>
+                {<img src={iconURL} draggable={false} />}
+            </foreignObject>
+            {props.status && (
+                <circle cx="27" cy="27" r="5" fill={useStatusColour(target)} />
+            )}
+            {props.voice && (
+                <foreignObject x="22" y="22" width="10" height="10">
+                    <VoiceIndicator status={props.voice}>
+                        {props.voice === "muted" && <MicrophoneOff size={6} />}
+                    </VoiceIndicator>
+                </foreignObject>
+            )}
+        </IconBase>
+    );
 }
diff --git a/src/components/common/user/UserShort.tsx b/src/components/common/user/UserShort.tsx
index 3c2b4aa..cd83a41 100644
--- a/src/components/common/user/UserShort.tsx
+++ b/src/components/common/user/UserShort.tsx
@@ -5,27 +5,27 @@ import { Text } from "preact-i18n";
 import UserIcon from "./UserIcon";
 
 export function Username({
-	user,
-	...otherProps
+    user,
+    ...otherProps
 }: { user?: User } & JSX.HTMLAttributes<HTMLElement>) {
-	return (
-		<span {...otherProps}>
-			{user?.username ?? <Text id="app.main.channel.unknown_user" />}
-		</span>
-	);
+    return (
+        <span {...otherProps}>
+            {user?.username ?? <Text id="app.main.channel.unknown_user" />}
+        </span>
+    );
 }
 
 export default function UserShort({
-	user,
-	size,
+    user,
+    size,
 }: {
-	user?: User;
-	size?: number;
+    user?: User;
+    size?: number;
 }) {
-	return (
-		<>
-			<UserIcon size={size ?? 24} target={user} />
-			<Username user={user} />
-		</>
-	);
+    return (
+        <>
+            <UserIcon size={size ?? 24} target={user} />
+            <Username user={user} />
+        </>
+    );
 }
diff --git a/src/components/common/user/UserStatus.tsx b/src/components/common/user/UserStatus.tsx
index d4b3f60..c909b1e 100644
--- a/src/components/common/user/UserStatus.tsx
+++ b/src/components/common/user/UserStatus.tsx
@@ -4,29 +4,29 @@ import { Users } from "revolt.js/dist/api/objects";
 import { Text } from "preact-i18n";
 
 interface Props {
-	user: User;
+    user: User;
 }
 
 export default function UserStatus({ user }: Props) {
-	if (user.online) {
-		if (user.status?.text) {
-			return <>{user.status?.text}</>;
-		}
+    if (user.online) {
+        if (user.status?.text) {
+            return <>{user.status?.text}</>;
+        }
 
-		if (user.status?.presence === Users.Presence.Busy) {
-			return <Text id="app.status.busy" />;
-		}
+        if (user.status?.presence === Users.Presence.Busy) {
+            return <Text id="app.status.busy" />;
+        }
 
-		if (user.status?.presence === Users.Presence.Idle) {
-			return <Text id="app.status.idle" />;
-		}
+        if (user.status?.presence === Users.Presence.Idle) {
+            return <Text id="app.status.idle" />;
+        }
 
-		if (user.status?.presence === Users.Presence.Invisible) {
-			return <Text id="app.status.offline" />;
-		}
+        if (user.status?.presence === Users.Presence.Invisible) {
+            return <Text id="app.status.offline" />;
+        }
 
-		return <Text id="app.status.online" />;
-	}
+        return <Text id="app.status.online" />;
+    }
 
-	return <Text id="app.status.offline" />;
+    return <Text id="app.status.offline" />;
 }
diff --git a/src/components/markdown/Markdown.tsx b/src/components/markdown/Markdown.tsx
index fbd274d..7e029b3 100644
--- a/src/components/markdown/Markdown.tsx
+++ b/src/components/markdown/Markdown.tsx
@@ -3,15 +3,15 @@ import { Suspense, lazy } from "preact/compat";
 const Renderer = lazy(() => import("./Renderer"));
 
 export interface MarkdownProps {
-	content?: string;
-	disallowBigEmoji?: boolean;
+    content?: string;
+    disallowBigEmoji?: boolean;
 }
 
 export default function Markdown(props: MarkdownProps) {
-	return (
-		// @ts-expect-error
-		<Suspense fallback={props.content}>
-			<Renderer {...props} />
-		</Suspense>
-	);
+    return (
+        // @ts-expect-error
+        <Suspense fallback={props.content}>
+            <Renderer {...props} />
+        </Suspense>
+    );
 }
diff --git a/src/components/markdown/Renderer.tsx b/src/components/markdown/Renderer.tsx
index a1dafb9..b501a68 100644
--- a/src/components/markdown/Renderer.tsx
+++ b/src/components/markdown/Renderer.tsx
@@ -26,167 +26,167 @@ import { MarkdownProps } from "./Markdown";
 
 // TODO: global.d.ts file for defining globals
 declare global {
-	interface Window {
-		copycode: (element: HTMLDivElement) => void;
-	}
+    interface Window {
+        copycode: (element: HTMLDivElement) => void;
+    }
 }
 
 // Handler for code block copy.
 if (typeof window !== "undefined") {
-	window.copycode = function (element: HTMLDivElement) {
-		try {
-			let code = element.parentElement?.parentElement?.children[1];
-			if (code) {
-				navigator.clipboard.writeText(code.textContent?.trim() ?? "");
-			}
-		} catch (e) {}
-	};
+    window.copycode = function (element: HTMLDivElement) {
+        try {
+            let code = element.parentElement?.parentElement?.children[1];
+            if (code) {
+                navigator.clipboard.writeText(code.textContent?.trim() ?? "");
+            }
+        } catch (e) {}
+    };
 }
 
 export const md: MarkdownIt = MarkdownIt({
-	breaks: true,
-	linkify: true,
-	highlight: (str, lang) => {
-		let v = Prism.languages[lang];
-		if (v) {
-			let out = Prism.highlight(str, v, lang);
-			return `<pre class="code"><div class="lang"><div onclick="copycode(this)">${lang}</div></div><code class="language-${lang}">${out}</code></pre>`;
-		}
-
-		return `<pre class="code"><code>${md.utils.escapeHtml(
-			str,
-		)}</code></pre>`;
-	},
+    breaks: true,
+    linkify: true,
+    highlight: (str, lang) => {
+        let v = Prism.languages[lang];
+        if (v) {
+            let out = Prism.highlight(str, v, lang);
+            return `<pre class="code"><div class="lang"><div onclick="copycode(this)">${lang}</div></div><code class="language-${lang}">${out}</code></pre>`;
+        }
+
+        return `<pre class="code"><code>${md.utils.escapeHtml(
+            str,
+        )}</code></pre>`;
+    },
 })
-	.disable("image")
-	.use(MarkdownEmoji, { defs: emojiDictionary })
-	.use(MarkdownSpoilers)
-	.use(MarkdownSup)
-	.use(MarkdownSub)
-	.use(MarkdownKatex, {
-		throwOnError: false,
-		maxExpand: 0,
-	});
+    .disable("image")
+    .use(MarkdownEmoji, { defs: emojiDictionary })
+    .use(MarkdownSpoilers)
+    .use(MarkdownSup)
+    .use(MarkdownSub)
+    .use(MarkdownKatex, {
+        throwOnError: false,
+        maxExpand: 0,
+    });
 
 // ? Force links to open _blank.
 // From: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
 const defaultRender =
-	md.renderer.rules.link_open ||
-	function (tokens, idx, options, _env, self) {
-		return self.renderToken(tokens, idx, options);
-	};
+    md.renderer.rules.link_open ||
+    function (tokens, idx, options, _env, self) {
+        return self.renderToken(tokens, idx, options);
+    };
 
 // TODO: global.d.ts file for defining globals
 declare global {
-	interface Window {
-		internalHandleURL: (element: HTMLAnchorElement) => void;
-	}
+    interface Window {
+        internalHandleURL: (element: HTMLAnchorElement) => void;
+    }
 }
 
 // Handler for internal links, pushes events to React using magic.
 if (typeof window !== "undefined") {
-	window.internalHandleURL = function (element: HTMLAnchorElement) {
-		const url = new URL(element.href, location.href);
-		const pathname = url.pathname;
-
-		if (pathname.startsWith("/@")) {
-			internalEmit("Intermediate", "openProfile", pathname.substr(2));
-		} else {
-			internalEmit("Intermediate", "navigate", pathname);
-		}
-	};
+    window.internalHandleURL = function (element: HTMLAnchorElement) {
+        const url = new URL(element.href, location.href);
+        const pathname = url.pathname;
+
+        if (pathname.startsWith("/@")) {
+            internalEmit("Intermediate", "openProfile", pathname.substr(2));
+        } else {
+            internalEmit("Intermediate", "navigate", pathname);
+        }
+    };
 }
 
 md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
-	let internal;
-	const hIndex = tokens[idx].attrIndex("href");
-	if (hIndex >= 0) {
-		try {
-			// For internal links, we should use our own handler to use react-router history.
-			// @ts-ignore
-			const href = tokens[idx].attrs[hIndex][1];
-			const url = new URL(href, location.href);
-
-			if (url.hostname === location.hostname) {
-				internal = true;
-				// I'm sorry.
-				tokens[idx].attrPush([
-					"onclick",
-					"internalHandleURL(this); return false",
-				]);
-
-				if (url.pathname.startsWith("/@")) {
-					tokens[idx].attrPush(["data-type", "mention"]);
-				}
-			}
-		} catch (err) {
-			// Ignore the error, treat as normal link.
-		}
-	}
-
-	if (!internal) {
-		// Add target=_blank for external links.
-		const aIndex = tokens[idx].attrIndex("target");
-
-		if (aIndex < 0) {
-			tokens[idx].attrPush(["target", "_blank"]);
-		} else {
-			try {
-				// @ts-ignore
-				tokens[idx].attrs[aIndex][1] = "_blank";
-			} catch (_) {}
-		}
-	}
-
-	return defaultRender(tokens, idx, options, env, self);
+    let internal;
+    const hIndex = tokens[idx].attrIndex("href");
+    if (hIndex >= 0) {
+        try {
+            // For internal links, we should use our own handler to use react-router history.
+            // @ts-ignore
+            const href = tokens[idx].attrs[hIndex][1];
+            const url = new URL(href, location.href);
+
+            if (url.hostname === location.hostname) {
+                internal = true;
+                // I'm sorry.
+                tokens[idx].attrPush([
+                    "onclick",
+                    "internalHandleURL(this); return false",
+                ]);
+
+                if (url.pathname.startsWith("/@")) {
+                    tokens[idx].attrPush(["data-type", "mention"]);
+                }
+            }
+        } catch (err) {
+            // Ignore the error, treat as normal link.
+        }
+    }
+
+    if (!internal) {
+        // Add target=_blank for external links.
+        const aIndex = tokens[idx].attrIndex("target");
+
+        if (aIndex < 0) {
+            tokens[idx].attrPush(["target", "_blank"]);
+        } else {
+            try {
+                // @ts-ignore
+                tokens[idx].attrs[aIndex][1] = "_blank";
+            } catch (_) {}
+        }
+    }
+
+    return defaultRender(tokens, idx, options, env, self);
 };
 
 md.renderer.rules.emoji = function (token, idx) {
-	return generateEmoji(token[idx].content);
+    return generateEmoji(token[idx].content);
 };
 
 const RE_TWEMOJI = /:(\w+):/g;
 
 export default function Renderer({ content, disallowBigEmoji }: MarkdownProps) {
-	const client = useContext(AppContext);
-	if (typeof content === "undefined") return null;
-	if (content.length === 0) return null;
-
-	// We replace the message with the mention at the time of render.
-	// We don't care if the mention changes.
-	let newContent = content.replace(
-		RE_MENTIONS,
-		(sub: string, ...args: any[]) => {
-			const id = args[0],
-				user = client.users.get(id);
-
-			if (user) {
-				return `[@${user.username}](/@${id})`;
-			}
-
-			return sub;
-		},
-	);
-
-	const useLargeEmojis = disallowBigEmoji
-		? false
-		: content.replace(RE_TWEMOJI, "").trim().length === 0;
-
-	return (
-		<span
-			className={styles.markdown}
-			dangerouslySetInnerHTML={{
-				__html: md.render(newContent),
-			}}
-			data-large-emojis={useLargeEmojis}
-			onClick={(ev) => {
-				if (ev.target) {
-					let element = ev.currentTarget;
-					if (element.classList.contains("spoiler")) {
-						element.classList.add("shown");
-					}
-				}
-			}}
-		/>
-	);
+    const client = useContext(AppContext);
+    if (typeof content === "undefined") return null;
+    if (content.length === 0) return null;
+
+    // We replace the message with the mention at the time of render.
+    // We don't care if the mention changes.
+    let newContent = content.replace(
+        RE_MENTIONS,
+        (sub: string, ...args: any[]) => {
+            const id = args[0],
+                user = client.users.get(id);
+
+            if (user) {
+                return `[@${user.username}](/@${id})`;
+            }
+
+            return sub;
+        },
+    );
+
+    const useLargeEmojis = disallowBigEmoji
+        ? false
+        : content.replace(RE_TWEMOJI, "").trim().length === 0;
+
+    return (
+        <span
+            className={styles.markdown}
+            dangerouslySetInnerHTML={{
+                __html: md.render(newContent),
+            }}
+            data-large-emojis={useLargeEmojis}
+            onClick={(ev) => {
+                if (ev.target) {
+                    let element = ev.currentTarget;
+                    if (element.classList.contains("spoiler")) {
+                        element.classList.add("shown");
+                    }
+                }
+            }}
+        />
+    );
 }
diff --git a/src/components/navigation/BottomNavigation.tsx b/src/components/navigation/BottomNavigation.tsx
index fd4629d..a34921d 100644
--- a/src/components/navigation/BottomNavigation.tsx
+++ b/src/components/navigation/BottomNavigation.tsx
@@ -13,84 +13,84 @@ import UserIcon from "../common/user/UserIcon";
 import IconButton from "../ui/IconButton";
 
 const NavigationBase = styled.div`
-	z-index: 100;
-	height: 50px;
-	display: flex;
-	background: var(--secondary-background);
+    z-index: 100;
+    height: 50px;
+    display: flex;
+    background: var(--secondary-background);
 `;
 
 const Button = styled.a<{ active: boolean }>`
-	flex: 1;
+    flex: 1;
 
-	> a,
-	> div,
-	> a > div {
-		width: 100%;
-		height: 100%;
-	}
+    > a,
+    > div,
+    > a > div {
+        width: 100%;
+        height: 100%;
+    }
 
-	${(props) =>
-		props.active &&
-		css`
-			background: var(--hover);
-		`}
+    ${(props) =>
+        props.active &&
+        css`
+            background: var(--hover);
+        `}
 `;
 
 interface Props {
-	lastOpened: LastOpened;
+    lastOpened: LastOpened;
 }
 
 export function BottomNavigation({ lastOpened }: Props) {
-	const user = useSelf();
-	const history = useHistory();
-	const path = useLocation().pathname;
+    const user = useSelf();
+    const history = useHistory();
+    const path = useLocation().pathname;
 
-	const channel_id = lastOpened["home"];
+    const channel_id = lastOpened["home"];
 
-	const friendsActive = path.startsWith("/friends");
-	const settingsActive = path.startsWith("/settings");
-	const homeActive = !(friendsActive || settingsActive);
+    const friendsActive = path.startsWith("/friends");
+    const settingsActive = path.startsWith("/settings");
+    const homeActive = !(friendsActive || settingsActive);
 
-	return (
-		<NavigationBase>
-			<Button active={homeActive}>
-				<IconButton
-					onClick={() => {
-						if (settingsActive) {
-							if (history.length > 0) {
-								history.goBack();
-							}
-						}
+    return (
+        <NavigationBase>
+            <Button active={homeActive}>
+                <IconButton
+                    onClick={() => {
+                        if (settingsActive) {
+                            if (history.length > 0) {
+                                history.goBack();
+                            }
+                        }
 
-						if (channel_id) {
-							history.push(`/channel/${channel_id}`);
-						} else {
-							history.push("/");
-						}
-					}}>
-					<Message size={24} />
-				</IconButton>
-			</Button>
-			<Button active={friendsActive}>
-				<ConditionalLink active={friendsActive} to="/friends">
-					<IconButton>
-						<Group size={25} />
-					</IconButton>
-				</ConditionalLink>
-			</Button>
-			<Button active={settingsActive}>
-				<ConditionalLink active={settingsActive} to="/settings">
-					<IconButton>
-						<UserIcon target={user} size={26} status={true} />
-					</IconButton>
-				</ConditionalLink>
-			</Button>
-		</NavigationBase>
-	);
+                        if (channel_id) {
+                            history.push(`/channel/${channel_id}`);
+                        } else {
+                            history.push("/");
+                        }
+                    }}>
+                    <Message size={24} />
+                </IconButton>
+            </Button>
+            <Button active={friendsActive}>
+                <ConditionalLink active={friendsActive} to="/friends">
+                    <IconButton>
+                        <Group size={25} />
+                    </IconButton>
+                </ConditionalLink>
+            </Button>
+            <Button active={settingsActive}>
+                <ConditionalLink active={settingsActive} to="/settings">
+                    <IconButton>
+                        <UserIcon target={user} size={26} status={true} />
+                    </IconButton>
+                </ConditionalLink>
+            </Button>
+        </NavigationBase>
+    );
 }
 
 export default connectState(BottomNavigation, (state) => {
-	return {
-		lastOpened: state.lastOpened,
-	};
+    return {
+        lastOpened: state.lastOpened,
+    };
 });
diff --git a/src/components/navigation/LeftSidebar.tsx b/src/components/navigation/LeftSidebar.tsx
index ccc675d..4561e5a 100644
--- a/src/components/navigation/LeftSidebar.tsx
+++ b/src/components/navigation/LeftSidebar.tsx
@@ -6,27 +6,27 @@ import ServerListSidebar from "./left/ServerListSidebar";
 import ServerSidebar from "./left/ServerSidebar";
 
 export default function LeftSidebar() {
-	return (
-		<SidebarBase>
-			<Switch>
-				<Route path="/settings" />
-				<Route path="/server/:server/channel/:channel">
-					<ServerListSidebar />
-					<ServerSidebar />
-				</Route>
-				<Route path="/server/:server">
-					<ServerListSidebar />
-					<ServerSidebar />
-				</Route>
-				<Route path="/channel/:channel">
-					<ServerListSidebar />
-					<HomeSidebar />
-				</Route>
-				<Route path="/">
-					<ServerListSidebar />
-					<HomeSidebar />
-				</Route>
-			</Switch>
-		</SidebarBase>
-	);
+    return (
+        <SidebarBase>
+            <Switch>
+                <Route path="/settings" />
+                <Route path="/server/:server/channel/:channel">
+                    <ServerListSidebar />
+                    <ServerSidebar />
+                </Route>
+                <Route path="/server/:server">
+                    <ServerListSidebar />
+                    <ServerSidebar />
+                </Route>
+                <Route path="/channel/:channel">
+                    <ServerListSidebar />
+                    <HomeSidebar />
+                </Route>
+                <Route path="/">
+                    <ServerListSidebar />
+                    <HomeSidebar />
+                </Route>
+            </Switch>
+        </SidebarBase>
+    );
 }
diff --git a/src/components/navigation/RightSidebar.tsx b/src/components/navigation/RightSidebar.tsx
index b1d6adf..edfac4d 100644
--- a/src/components/navigation/RightSidebar.tsx
+++ b/src/components/navigation/RightSidebar.tsx
@@ -4,16 +4,16 @@ import SidebarBase from "./SidebarBase";
 import MemberSidebar from "./right/MemberSidebar";
 
 export default function RightSidebar() {
-	return (
-		<SidebarBase>
-			<Switch>
-				<Route path="/server/:server/channel/:channel">
-					<MemberSidebar />
-				</Route>
-				<Route path="/channel/:channel">
-					<MemberSidebar />
-				</Route>
-			</Switch>
-		</SidebarBase>
-	);
+    return (
+        <SidebarBase>
+            <Switch>
+                <Route path="/server/:server/channel/:channel">
+                    <MemberSidebar />
+                </Route>
+                <Route path="/channel/:channel">
+                    <MemberSidebar />
+                </Route>
+            </Switch>
+        </SidebarBase>
+    );
 }
diff --git a/src/components/navigation/SidebarBase.tsx b/src/components/navigation/SidebarBase.tsx
index 8d5cd60..064c7b3 100644
--- a/src/components/navigation/SidebarBase.tsx
+++ b/src/components/navigation/SidebarBase.tsx
@@ -3,36 +3,36 @@ import styled, { css } from "styled-components";
 import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
 
 export default styled.div`
-	height: 100%;
-	display: flex;
-	user-select: none;
-	flex-direction: row;
-	align-items: stretch;
+    height: 100%;
+    display: flex;
+    user-select: none;
+    flex-direction: row;
+    align-items: stretch;
 `;
 
 export const GenericSidebarBase = styled.div<{ padding?: boolean }>`
-	height: 100%;
-	width: 240px;
-	display: flex;
-	flex-shrink: 0;
-	flex-direction: column;
-	background: var(--secondary-background);
-	border-end-start-radius: 8px;
+    height: 100%;
+    width: 240px;
+    display: flex;
+    flex-shrink: 0;
+    flex-direction: column;
+    background: var(--secondary-background);
+    border-end-start-radius: 8px;
 
-	${(props) =>
-		props.padding &&
-		isTouchscreenDevice &&
-		css`
-			padding-bottom: 50px;
-		`}
+    ${(props) =>
+        props.padding &&
+        isTouchscreenDevice &&
+        css`
+            padding-bottom: 50px;
+        `}
 `;
 
 export const GenericSidebarList = styled.div`
-	padding: 6px;
-	flex-grow: 1;
-	overflow-y: scroll;
+    padding: 6px;
+    flex-grow: 1;
+    overflow-y: scroll;
 
-	> img {
-		width: 100%;
-	}
+    > img {
+        width: 100%;
+    }
 `;
diff --git a/src/components/navigation/items/ButtonItem.tsx b/src/components/navigation/items/ButtonItem.tsx
index 5da41cf..4d841a2 100644
--- a/src/components/navigation/items/ButtonItem.tsx
+++ b/src/components/navigation/items/ButtonItem.tsx
@@ -20,204 +20,204 @@ import IconButton from "../../ui/IconButton";
 import { Children } from "../../../types/Preact";
 
 type CommonProps = Omit<
-	JSX.HTMLAttributes<HTMLDivElement>,
-	"children" | "as"
+    JSX.HTMLAttributes<HTMLDivElement>,
+    "children" | "as"
 > & {
-	active?: boolean;
-	alert?: "unread" | "mention";
-	alertCount?: number;
+    active?: boolean;
+    alert?: "unread" | "mention";
+    alertCount?: number;
 };
 
 type UserProps = CommonProps & {
-	user: Users.User;
-	context?: Channels.Channel;
-	channel?: Channels.DirectMessageChannel;
+    user: Users.User;
+    context?: Channels.Channel;
+    channel?: Channels.DirectMessageChannel;
 };
 
 export function UserButton(props: UserProps) {
-	const { active, alert, alertCount, user, context, channel, ...divProps } =
-		props;
-	const { openScreen } = useIntermediate();
-
-	return (
-		<div
-			{...divProps}
-			className={classNames(styles.item, styles.user)}
-			data-active={active}
-			data-alert={typeof alert === "string"}
-			data-online={
-				typeof channel !== "undefined" ||
-				(user.online &&
-					user.status?.presence !== Users.Presence.Invisible)
-			}
-			onContextMenu={attachContextMenu("Menu", {
-				user: user._id,
-				channel: channel?._id,
-				unread: alert,
-				contextualChannel: context?._id,
-			})}>
-			<UserIcon
-				className={styles.avatar}
-				target={user}
-				size={32}
-				status
-			/>
-			<div className={styles.name}>
-				<div>{user.username}</div>
-				{
-					<div className={styles.subText}>
-						{channel?.last_message && alert ? (
-							channel.last_message.short
-						) : (
-							<UserStatus user={user} />
-						)}
-					</div>
-				}
-			</div>
-			<div className={styles.button}>
-				{context?.channel_type === "Group" &&
-					context.owner === user._id && (
-						<Localizer>
-							<Tooltip
-								content={<Text id="app.main.groups.owner" />}>
-								<Crown size={20} />
-							</Tooltip>
-						</Localizer>
-					)}
-				{alert && (
-					<div className={styles.alert} data-style={alert}>
-						{alertCount}
-					</div>
-				)}
-				{!isTouchscreenDevice && channel && (
-					<IconButton
-						className={styles.icon}
-						onClick={(e) =>
-							stopPropagation(e) &&
-							openScreen({
-								id: "special_prompt",
-								type: "close_dm",
-								target: channel,
-							})
-						}>
-						<X size={24} />
-					</IconButton>
-				)}
-			</div>
-		</div>
-	);
+    const { active, alert, alertCount, user, context, channel, ...divProps } =
+        props;
+    const { openScreen } = useIntermediate();
+
+    return (
+        <div
+            {...divProps}
+            className={classNames(styles.item, styles.user)}
+            data-active={active}
+            data-alert={typeof alert === "string"}
+            data-online={
+                typeof channel !== "undefined" ||
+                (user.online &&
+                    user.status?.presence !== Users.Presence.Invisible)
+            }
+            onContextMenu={attachContextMenu("Menu", {
+                user: user._id,
+                channel: channel?._id,
+                unread: alert,
+                contextualChannel: context?._id,
+            })}>
+            <UserIcon
+                className={styles.avatar}
+                target={user}
+                size={32}
+                status
+            />
+            <div className={styles.name}>
+                <div>{user.username}</div>
+                {
+                    <div className={styles.subText}>
+                        {channel?.last_message && alert ? (
+                            channel.last_message.short
+                        ) : (
+                            <UserStatus user={user} />
+                        )}
+                    </div>
+                }
+            </div>
+            <div className={styles.button}>
+                {context?.channel_type === "Group" &&
+                    context.owner === user._id && (
+                        <Localizer>
+                            <Tooltip
+                                content={<Text id="app.main.groups.owner" />}>
+                                <Crown size={20} />
+                            </Tooltip>
+                        </Localizer>
+                    )}
+                {alert && (
+                    <div className={styles.alert} data-style={alert}>
+                        {alertCount}
+                    </div>
+                )}
+                {!isTouchscreenDevice && channel && (
+                    <IconButton
+                        className={styles.icon}
+                        onClick={(e) =>
+                            stopPropagation(e) &&
+                            openScreen({
+                                id: "special_prompt",
+                                type: "close_dm",
+                                target: channel,
+                            })
+                        }>
+                        <X size={24} />
+                    </IconButton>
+                )}
+            </div>
+        </div>
+    );
 }
 
 type ChannelProps = CommonProps & {
-	channel: Channels.Channel & { unread?: string };
-	user?: Users.User;
-	compact?: boolean;
+    channel: Channels.Channel & { unread?: string };
+    user?: Users.User;
+    compact?: boolean;
 };
 
 export function ChannelButton(props: ChannelProps) {
-	const { active, alert, alertCount, channel, user, compact, ...divProps } =
-		props;
-
-	if (channel.channel_type === "SavedMessages") throw "Invalid channel type.";
-	if (channel.channel_type === "DirectMessage") {
-		if (typeof user === "undefined") throw "No user provided.";
-		return <UserButton {...{ active, alert, channel, user }} />;
-	}
-
-	const { openScreen } = useIntermediate();
-
-	return (
-		<div
-			{...divProps}
-			data-active={active}
-			data-alert={typeof alert === "string"}
-			aria-label={{}} /*FIXME: ADD ARIA LABEL*/
-			className={classNames(styles.item, { [styles.compact]: compact })}
-			onContextMenu={attachContextMenu("Menu", {
-				channel: channel._id,
-				unread: typeof channel.unread !== "undefined",
-			})}>
-			<ChannelIcon
-				className={styles.avatar}
-				target={channel}
-				size={compact ? 24 : 32}
-			/>
-			<div className={styles.name}>
-				<div>{channel.name}</div>
-				{channel.channel_type === "Group" && (
-					<div className={styles.subText}>
-						{channel.last_message && alert ? (
-							channel.last_message.short
-						) : (
-							<Text
-								id="quantities.members"
-								plural={channel.recipients.length}
-								fields={{ count: channel.recipients.length }}
-							/>
-						)}
-					</div>
-				)}
-			</div>
-			<div className={styles.button}>
-				{alert && (
-					<div className={styles.alert} data-style={alert}>
-						{alertCount}
-					</div>
-				)}
-				{!isTouchscreenDevice && channel.channel_type === "Group" && (
-					<IconButton
-						className={styles.icon}
-						onClick={() =>
-							openScreen({
-								id: "special_prompt",
-								type: "leave_group",
-								target: channel,
-							})
-						}>
-						<X size={24} />
-					</IconButton>
-				)}
-			</div>
-		</div>
-	);
+    const { active, alert, alertCount, channel, user, compact, ...divProps } =
+        props;
+
+    if (channel.channel_type === "SavedMessages") throw "Invalid channel type.";
+    if (channel.channel_type === "DirectMessage") {
+        if (typeof user === "undefined") throw "No user provided.";
+        return <UserButton {...{ active, alert, channel, user }} />;
+    }
+
+    const { openScreen } = useIntermediate();
+
+    return (
+        <div
+            {...divProps}
+            data-active={active}
+            data-alert={typeof alert === "string"}
+            aria-label={{}} /*FIXME: ADD ARIA LABEL*/
+            className={classNames(styles.item, { [styles.compact]: compact })}
+            onContextMenu={attachContextMenu("Menu", {
+                channel: channel._id,
+                unread: typeof channel.unread !== "undefined",
+            })}>
+            <ChannelIcon
+                className={styles.avatar}
+                target={channel}
+                size={compact ? 24 : 32}
+            />
+            <div className={styles.name}>
+                <div>{channel.name}</div>
+                {channel.channel_type === "Group" && (
+                    <div className={styles.subText}>
+                        {channel.last_message && alert ? (
+                            channel.last_message.short
+                        ) : (
+                            <Text
+                                id="quantities.members"
+                                plural={channel.recipients.length}
+                                fields={{ count: channel.recipients.length }}
+                            />
+                        )}
+                    </div>
+                )}
+            </div>
+            <div className={styles.button}>
+                {alert && (
+                    <div className={styles.alert} data-style={alert}>
+                        {alertCount}
+                    </div>
+                )}
+                {!isTouchscreenDevice && channel.channel_type === "Group" && (
+                    <IconButton
+                        className={styles.icon}
+                        onClick={() =>
+                            openScreen({
+                                id: "special_prompt",
+                                type: "leave_group",
+                                target: channel,
+                            })
+                        }>
+                        <X size={24} />
+                    </IconButton>
+                )}
+            </div>
+        </div>
+    );
 }
 
 type ButtonProps = CommonProps & {
-	onClick?: () => void;
-	children?: Children;
-	className?: string;
-	compact?: boolean;
+    onClick?: () => void;
+    children?: Children;
+    className?: string;
+    compact?: boolean;
 };
 
 export default function ButtonItem(props: ButtonProps) {
-	const {
-		active,
-		alert,
-		alertCount,
-		onClick,
-		className,
-		children,
-		compact,
-		...divProps
-	} = props;
-
-	return (
-		<div
-			{...divProps}
-			className={classNames(
-				styles.item,
-				{ [styles.compact]: compact, [styles.normal]: !compact },
-				className,
-			)}
-			onClick={onClick}
-			data-active={active}
-			data-alert={typeof alert === "string"}>
-			<div className={styles.content}>{children}</div>
-			{alert && (
-				<div className={styles.alert} data-style={alert}>
-					{alertCount}
-				</div>
-			)}
-		</div>
-	);
+    const {
+        active,
+        alert,
+        alertCount,
+        onClick,
+        className,
+        children,
+        compact,
+        ...divProps
+    } = props;
+
+    return (
+        <div
+            {...divProps}
+            className={classNames(
+                styles.item,
+                { [styles.compact]: compact, [styles.normal]: !compact },
+                className,
+            )}
+            onClick={onClick}
+            data-active={active}
+            data-alert={typeof alert === "string"}>
+            <div className={styles.content}>{children}</div>
+            {alert && (
+                <div className={styles.alert} data-style={alert}>
+                    {alertCount}
+                </div>
+            )}
+        </div>
+    );
 }
diff --git a/src/components/navigation/items/ConnectionStatus.tsx b/src/components/navigation/items/ConnectionStatus.tsx
index 667f2da..901ef62 100644
--- a/src/components/navigation/items/ConnectionStatus.tsx
+++ b/src/components/navigation/items/ConnectionStatus.tsx
@@ -2,39 +2,39 @@ import { Text } from "preact-i18n";
 import { useContext } from "preact/hooks";
 
 import {
-	ClientStatus,
-	StatusContext,
+    ClientStatus,
+    StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
 
 import Banner from "../../ui/Banner";
 
 export default function ConnectionStatus() {
-	const status = useContext(StatusContext);
+    const status = useContext(StatusContext);
 
-	if (status === ClientStatus.OFFLINE) {
-		return (
-			<Banner>
-				<Text id="app.special.status.offline" />
-			</Banner>
-		);
-	} else if (status === ClientStatus.DISCONNECTED) {
-		return (
-			<Banner>
-				<Text id="app.special.status.disconnected" />
-			</Banner>
-		);
-	} else if (status === ClientStatus.CONNECTING) {
-		return (
-			<Banner>
-				<Text id="app.special.status.connecting" />
-			</Banner>
-		);
-	} else if (status === ClientStatus.RECONNECTING) {
-		return (
-			<Banner>
-				<Text id="app.special.status.reconnecting" />
-			</Banner>
-		);
-	}
-	return null;
+    if (status === ClientStatus.OFFLINE) {
+        return (
+            <Banner>
+                <Text id="app.special.status.offline" />
+            </Banner>
+        );
+    } else if (status === ClientStatus.DISCONNECTED) {
+        return (
+            <Banner>
+                <Text id="app.special.status.disconnected" />
+            </Banner>
+        );
+    } else if (status === ClientStatus.CONNECTING) {
+        return (
+            <Banner>
+                <Text id="app.special.status.connecting" />
+            </Banner>
+        );
+    } else if (status === ClientStatus.RECONNECTING) {
+        return (
+            <Banner>
+                <Text id="app.special.status.reconnecting" />
+            </Banner>
+        );
+    }
+    return null;
 }
diff --git a/src/components/navigation/left/HomeSidebar.tsx b/src/components/navigation/left/HomeSidebar.tsx
index a0de138..fe951e1 100644
--- a/src/components/navigation/left/HomeSidebar.tsx
+++ b/src/components/navigation/left/HomeSidebar.tsx
@@ -1,8 +1,8 @@
 import {
-	Home,
-	UserDetail,
-	Wrench,
-	Notepad,
+    Home,
+    UserDetail,
+    Wrench,
+    Notepad,
 } from "@styled-icons/boxicons-solid";
 import { Link, Redirect, useLocation, useParams } from "react-router-dom";
 import { Channels } from "revolt.js/dist/api/objects";
@@ -22,9 +22,9 @@ import { Unreads } from "../../../redux/reducers/unreads";
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import {
-	useDMs,
-	useForceUpdate,
-	useUsers,
+    useDMs,
+    useForceUpdate,
+    useUsers,
 } from "../../../context/revoltjs/hooks";
 
 import UserHeader from "../../common/user/UserHeader";
@@ -37,157 +37,157 @@ import ButtonItem, { ChannelButton } from "../items/ButtonItem";
 import ConnectionStatus from "../items/ConnectionStatus";
 
 type Props = {
-	unreads: Unreads;
+    unreads: Unreads;
 };
 
 function HomeSidebar(props: Props) {
-	const { pathname } = useLocation();
-	const client = useContext(AppContext);
-	const { channel } = useParams<{ channel: string }>();
-	const { openScreen } = useIntermediate();
-
-	const ctx = useForceUpdate();
-	const channels = useDMs(ctx);
-
-	const obj = channels.find((x) => x?._id === channel);
-	if (channel && !obj) return <Redirect to="/" />;
-	if (obj) useUnreads({ ...props, channel: obj });
-
-	useEffect(() => {
-		if (!channel) return;
-
-		dispatch({
-			type: "LAST_OPENED_SET",
-			parent: "home",
-			child: channel,
-		});
-	}, [channel]);
-
-	const channelsArr = channels
-		.filter((x) => x.channel_type !== "SavedMessages")
-		.map((x) => mapChannelWithUnread(x, props.unreads));
-
-	const users = useUsers(
-		(
-			channelsArr as (
-				| Channels.DirectMessageChannel
-				| Channels.GroupChannel
-			)[]
-		).reduce((prev: any, cur) => [...prev, ...cur.recipients], []),
-		ctx,
-	);
-
-	channelsArr.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
-
-	return (
-		<GenericSidebarBase padding>
-			<UserHeader user={client.user!} />
-			<ConnectionStatus />
-			<GenericSidebarList>
-				{!isTouchscreenDevice && (
-					<>
-						<ConditionalLink active={pathname === "/"} to="/">
-							<ButtonItem active={pathname === "/"}>
-								<Home size={20} />
-								<span>
-									<Text id="app.navigation.tabs.home" />
-								</span>
-							</ButtonItem>
-						</ConditionalLink>
-						<ConditionalLink
-							active={pathname === "/friends"}
-							to="/friends">
-							<ButtonItem
-								active={pathname === "/friends"}
-								alert={
-									typeof users.find(
-										(user) =>
-											user?.relationship ===
-											UsersNS.Relationship.Incoming,
-									) !== "undefined"
-										? "unread"
-										: undefined
-								}>
-								<UserDetail size={20} />
-								<span>
-									<Text id="app.navigation.tabs.friends" />
-								</span>
-							</ButtonItem>
-						</ConditionalLink>
-					</>
-				)}
-				<ConditionalLink
-					active={obj?.channel_type === "SavedMessages"}
-					to="/open/saved">
-					<ButtonItem active={obj?.channel_type === "SavedMessages"}>
-						<Notepad size={20} />
-						<span>
-							<Text id="app.navigation.tabs.saved" />
-						</span>
-					</ButtonItem>
-				</ConditionalLink>
-				{import.meta.env.DEV && (
-					<Link to="/dev">
-						<ButtonItem active={pathname === "/dev"}>
-							<Wrench size={20} />
-							<span>
-								<Text id="app.navigation.tabs.dev" />
-							</span>
-						</ButtonItem>
-					</Link>
-				)}
-				<Category
-					text={<Text id="app.main.categories.conversations" />}
-					action={() =>
-						openScreen({
-							id: "special_input",
-							type: "create_group",
-						})
-					}
-				/>
-				{channelsArr.length === 0 && <img src={placeholderSVG} />}
-				{channelsArr.map((x) => {
-					let user;
-					if (x.channel_type === "DirectMessage") {
-						if (!x.active) return null;
-
-						let recipient = client.channels.getRecipient(x._id);
-						user = users.find((x) => x?._id === recipient);
-
-						if (!user) {
-							console.warn(
-								`Skipped DM ${x._id} because user was missing.`,
-							);
-							return null;
-						}
-					}
-
-					return (
-						<ConditionalLink
-							active={x._id === channel}
-							to={`/channel/${x._id}`}>
-							<ChannelButton
-								user={user}
-								channel={x}
-								alert={x.unread}
-								alertCount={x.alertCount}
-								active={x._id === channel}
-							/>
-						</ConditionalLink>
-					);
-				})}
-				<PaintCounter />
-			</GenericSidebarList>
-		</GenericSidebarBase>
-	);
+    const { pathname } = useLocation();
+    const client = useContext(AppContext);
+    const { channel } = useParams<{ channel: string }>();
+    const { openScreen } = useIntermediate();
+
+    const ctx = useForceUpdate();
+    const channels = useDMs(ctx);
+
+    const obj = channels.find((x) => x?._id === channel);
+    if (channel && !obj) return <Redirect to="/" />;
+    if (obj) useUnreads({ ...props, channel: obj });
+
+    useEffect(() => {
+        if (!channel) return;
+
+        dispatch({
+            type: "LAST_OPENED_SET",
+            parent: "home",
+            child: channel,
+        });
+    }, [channel]);
+
+    const channelsArr = channels
+        .filter((x) => x.channel_type !== "SavedMessages")
+        .map((x) => mapChannelWithUnread(x, props.unreads));
+
+    const users = useUsers(
+        (
+            channelsArr as (
+                | Channels.DirectMessageChannel
+                | Channels.GroupChannel
+            )[]
+        ).reduce((prev: any, cur) => [...prev, ...cur.recipients], []),
+        ctx,
+    );
+
+    channelsArr.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
+
+    return (
+        <GenericSidebarBase padding>
+            <UserHeader user={client.user!} />
+            <ConnectionStatus />
+            <GenericSidebarList>
+                {!isTouchscreenDevice && (
+                    <>
+                        <ConditionalLink active={pathname === "/"} to="/">
+                            <ButtonItem active={pathname === "/"}>
+                                <Home size={20} />
+                                <span>
+                                    <Text id="app.navigation.tabs.home" />
+                                </span>
+                            </ButtonItem>
+                        </ConditionalLink>
+                        <ConditionalLink
+                            active={pathname === "/friends"}
+                            to="/friends">
+                            <ButtonItem
+                                active={pathname === "/friends"}
+                                alert={
+                                    typeof users.find(
+                                        (user) =>
+                                            user?.relationship ===
+                                            UsersNS.Relationship.Incoming,
+                                    ) !== "undefined"
+                                        ? "unread"
+                                        : undefined
+                                }>
+                                <UserDetail size={20} />
+                                <span>
+                                    <Text id="app.navigation.tabs.friends" />
+                                </span>
+                            </ButtonItem>
+                        </ConditionalLink>
+                    </>
+                )}
+                <ConditionalLink
+                    active={obj?.channel_type === "SavedMessages"}
+                    to="/open/saved">
+                    <ButtonItem active={obj?.channel_type === "SavedMessages"}>
+                        <Notepad size={20} />
+                        <span>
+                            <Text id="app.navigation.tabs.saved" />
+                        </span>
+                    </ButtonItem>
+                </ConditionalLink>
+                {import.meta.env.DEV && (
+                    <Link to="/dev">
+                        <ButtonItem active={pathname === "/dev"}>
+                            <Wrench size={20} />
+                            <span>
+                                <Text id="app.navigation.tabs.dev" />
+                            </span>
+                        </ButtonItem>
+                    </Link>
+                )}
+                <Category
+                    text={<Text id="app.main.categories.conversations" />}
+                    action={() =>
+                        openScreen({
+                            id: "special_input",
+                            type: "create_group",
+                        })
+                    }
+                />
+                {channelsArr.length === 0 && <img src={placeholderSVG} />}
+                {channelsArr.map((x) => {
+                    let user;
+                    if (x.channel_type === "DirectMessage") {
+                        if (!x.active) return null;
+
+                        let recipient = client.channels.getRecipient(x._id);
+                        user = users.find((x) => x?._id === recipient);
+
+                        if (!user) {
+                            console.warn(
+                                `Skipped DM ${x._id} because user was missing.`,
+                            );
+                            return null;
+                        }
+                    }
+
+                    return (
+                        <ConditionalLink
+                            active={x._id === channel}
+                            to={`/channel/${x._id}`}>
+                            <ChannelButton
+                                user={user}
+                                channel={x}
+                                alert={x.unread}
+                                alertCount={x.alertCount}
+                                active={x._id === channel}
+                            />
+                        </ConditionalLink>
+                    );
+                })}
+                <PaintCounter />
+            </GenericSidebarList>
+        </GenericSidebarBase>
+    );
 }
 
 export default connectState(
-	HomeSidebar,
-	(state) => {
-		return {
-			unreads: state.unreads,
-		};
-	},
-	true,
+    HomeSidebar,
+    (state) => {
+        return {
+            unreads: state.unreads,
+        };
+    },
+    true,
 );
diff --git a/src/components/navigation/left/ServerListSidebar.tsx b/src/components/navigation/left/ServerListSidebar.tsx
index f75ac8e..838b74c 100644
--- a/src/components/navigation/left/ServerListSidebar.tsx
+++ b/src/components/navigation/left/ServerListSidebar.tsx
@@ -15,10 +15,10 @@ import { Unreads } from "../../../redux/reducers/unreads";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import {
-	useChannels,
-	useForceUpdate,
-	useSelf,
-	useServers,
+    useChannels,
+    useForceUpdate,
+    useSelf,
+    useServers,
 } from "../../../context/revoltjs/hooks";
 
 import ServerIcon from "../../common/ServerIcon";
@@ -31,268 +31,268 @@ import { mapChannelWithUnread } from "./common";
 import { Children } from "../../../types/Preact";
 
 function Icon({
-	children,
-	unread,
-	size,
+    children,
+    unread,
+    size,
 }: {
-	children: Children;
-	unread?: "mention" | "unread";
-	size: number;
+    children: Children;
+    unread?: "mention" | "unread";
+    size: number;
 }) {
-	return (
-		<svg width={size} height={size} aria-hidden="true" viewBox="0 0 32 32">
-			<use href="#serverIndicator" />
-			<foreignObject
-				x="0"
-				y="0"
-				width="32"
-				height="32"
-				mask={unread ? "url(#server)" : undefined}>
-				{children}
-			</foreignObject>
-			{unread === "unread" && (
-				<circle cx="27" cy="5" r="5" fill={"white"} />
-			)}
-			{unread === "mention" && (
-				<circle cx="27" cy="5" r="5" fill={"red"} />
-			)}
-		</svg>
-	);
+    return (
+        <svg width={size} height={size} aria-hidden="true" viewBox="0 0 32 32">
+            <use href="#serverIndicator" />
+            <foreignObject
+                x="0"
+                y="0"
+                width="32"
+                height="32"
+                mask={unread ? "url(#server)" : undefined}>
+                {children}
+            </foreignObject>
+            {unread === "unread" && (
+                <circle cx="27" cy="5" r="5" fill={"white"} />
+            )}
+            {unread === "mention" && (
+                <circle cx="27" cy="5" r="5" fill={"red"} />
+            )}
+        </svg>
+    );
 }
 
 const ServersBase = styled.div`
-	width: 56px;
-	height: 100%;
-	display: flex;
-	flex-direction: column;
-
-	${isTouchscreenDevice &&
-	css`
-		padding-bottom: 50px;
-	`}
+    width: 56px;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+
+    ${isTouchscreenDevice &&
+    css`
+        padding-bottom: 50px;
+    `}
 `;
 
 const ServerList = styled.div`
-	flex-grow: 1;
-	display: flex;
-	overflow-y: scroll;
-	padding-bottom: 48px;
-	flex-direction: column;
-	// border-right: 2px solid var(--accent);
-
-	scrollbar-width: none;
-
-	> :first-child > svg {
-		margin: 6px 0 6px 4px;
-	}
-
-	&::-webkit-scrollbar {
-		width: 0px;
-	}
+    flex-grow: 1;
+    display: flex;
+    overflow-y: scroll;
+    padding-bottom: 48px;
+    flex-direction: column;
+    // border-right: 2px solid var(--accent);
+
+    scrollbar-width: none;
+
+    > :first-child > svg {
+        margin: 6px 0 6px 4px;
+    }
+
+    &::-webkit-scrollbar {
+        width: 0px;
+    }
 `;
 
 const ServerEntry = styled.div<{ active: boolean; home?: boolean }>`
-	height: 58px;
-	display: flex;
-	align-items: center;
-	justify-content: flex-end;
-
-	> * {
-		// outline: 1px solid red;
-	}
-
-	> div {
-		width: 46px;
-		height: 46px;
-		display: grid;
-		place-items: center;
-
-		border-start-start-radius: 50%;
-		border-end-start-radius: 50%;
-
-		&:active {
-			transform: translateY(1px);
-		}
-
-		${(props) =>
-			props.active &&
-			css`
-				background: var(--sidebar-active);
-				&:active {
-					transform: none;
-				}
-			`}
-	}
-
-	span {
-		width: 6px;
-		height: 46px;
-
-		${(props) =>
-			props.active &&
-			css`
-				background-color: var(--sidebar-active);
-
-				&::before,
-				&::after {
-					// outline: 1px solid blue;
-				}
-
-				&::before,
-				&::after {
-					content: "";
-					display: block;
-					position: relative;
-
-					width: 31px;
-					height: 72px;
-					margin-top: -72px;
-					margin-left: -25px;
-					z-index: -1;
-
-					background-color: var(--background);
-					border-bottom-right-radius: 32px;
-
-					box-shadow: 0 32px 0 0 var(--sidebar-active);
-				}
-
-				&::after {
-					transform: scaleY(-1) translateY(-118px);
-				}
-			`}
-	}
-
-	${(props) =>
-		(!props.active || props.home) &&
-		css`
-			cursor: pointer;
-		`}
+    height: 58px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+
+    > * {
+        // outline: 1px solid red;
+    }
+
+    > div {
+        width: 46px;
+        height: 46px;
+        display: grid;
+        place-items: center;
+
+        border-start-start-radius: 50%;
+        border-end-start-radius: 50%;
+
+        &:active {
+            transform: translateY(1px);
+        }
+
+        ${(props) =>
+            props.active &&
+            css`
+                background: var(--sidebar-active);
+                &:active {
+                    transform: none;
+                }
+            `}
+    }
+
+    span {
+        width: 6px;
+        height: 46px;
+
+        ${(props) =>
+            props.active &&
+            css`
+                background-color: var(--sidebar-active);
+
+                &::before,
+                &::after {
+                    // outline: 1px solid blue;
+                }
+
+                &::before,
+                &::after {
+                    content: "";
+                    display: block;
+                    position: relative;
+
+                    width: 31px;
+                    height: 72px;
+                    margin-top: -72px;
+                    margin-left: -25px;
+                    z-index: -1;
+
+                    background-color: var(--background);
+                    border-bottom-right-radius: 32px;
+
+                    box-shadow: 0 32px 0 0 var(--sidebar-active);
+                }
+
+                &::after {
+                    transform: scaleY(-1) translateY(-118px);
+                }
+            `}
+    }
+
+    ${(props) =>
+        (!props.active || props.home) &&
+        css`
+            cursor: pointer;
+        `}
 `;
 
 interface Props {
-	unreads: Unreads;
-	lastOpened: LastOpened;
+    unreads: Unreads;
+    lastOpened: LastOpened;
 }
 
 export function ServerListSidebar({ unreads, lastOpened }: Props) {
-	const ctx = useForceUpdate();
-	const self = useSelf(ctx);
-	const activeServers = useServers(undefined, ctx) as Servers.Server[];
-	const channels = (useChannels(undefined, ctx) as Channel[]).map((x) =>
-		mapChannelWithUnread(x, unreads),
-	);
-
-	const unreadChannels = channels.filter((x) => x.unread).map((x) => x._id);
-
-	const servers = activeServers.map((server) => {
-		let alertCount = 0;
-		for (let id of server.channels) {
-			let channel = channels.find((x) => x._id === id);
-			if (channel?.alertCount) {
-				alertCount += channel.alertCount;
-			}
-		}
-
-		return {
-			...server,
-			unread: (typeof server.channels.find((x) =>
-				unreadChannels.includes(x),
-			) !== "undefined"
-				? alertCount > 0
-					? "mention"
-					: "unread"
-				: undefined) as "mention" | "unread" | undefined,
-			alertCount,
-		};
-	});
-
-	const path = useLocation().pathname;
-	const { server: server_id } = useParams<{ server?: string }>();
-	const server = servers.find((x) => x!._id == server_id);
-
-	const { openScreen } = useIntermediate();
-
-	let homeUnread: "mention" | "unread" | undefined;
-	let alertCount = 0;
-	for (let x of channels) {
-		if (
-			((x.channel_type === "DirectMessage" && x.active) ||
-				x.channel_type === "Group") &&
-			x.unread
-		) {
-			homeUnread = "unread";
-			alertCount += x.alertCount ?? 0;
-		}
-	}
-
-	if (alertCount > 0) homeUnread = "mention";
-	const homeActive =
-		typeof server === "undefined" && !path.startsWith("/invite");
-
-	return (
-		<ServersBase>
-			<ServerList>
-				<ConditionalLink
-					active={homeActive}
-					to={lastOpened.home ? `/channel/${lastOpened.home}` : "/"}>
-					<ServerEntry home active={homeActive}>
-						<div
-							onContextMenu={attachContextMenu("Status")}
-							onClick={() =>
-								homeActive && openContextMenu("Status")
-							}>
-							<Icon size={42} unread={homeUnread}>
-								<UserIcon target={self} size={32} status />
-							</Icon>
-						</div>
-						<span />
-					</ServerEntry>
-				</ConditionalLink>
-				<LineDivider />
-				{servers.map((entry) => {
-					const active = entry!._id === server?._id;
-					const id = lastOpened[entry!._id];
-
-					return (
-						<ConditionalLink
-							active={active}
-							to={
-								`/server/${entry!._id}` +
-								(id ? `/channel/${id}` : "")
-							}>
-							<ServerEntry
-								active={active}
-								onContextMenu={attachContextMenu("Menu", {
-									server: entry!._id,
-								})}>
-								<Tooltip content={entry.name} placement="right">
-									<Icon size={42} unread={entry.unread}>
-										<ServerIcon size={32} target={entry} />
-									</Icon>
-								</Tooltip>
-								<span />
-							</ServerEntry>
-						</ConditionalLink>
-					);
-				})}
-				<IconButton
-					onClick={() =>
-						openScreen({
-							id: "special_input",
-							type: "create_server",
-						})
-					}>
-					<Plus size={36} />
-				</IconButton>
-				<PaintCounter small />
-			</ServerList>
-		</ServersBase>
-	);
+    const ctx = useForceUpdate();
+    const self = useSelf(ctx);
+    const activeServers = useServers(undefined, ctx) as Servers.Server[];
+    const channels = (useChannels(undefined, ctx) as Channel[]).map((x) =>
+        mapChannelWithUnread(x, unreads),
+    );
+
+    const unreadChannels = channels.filter((x) => x.unread).map((x) => x._id);
+
+    const servers = activeServers.map((server) => {
+        let alertCount = 0;
+        for (let id of server.channels) {
+            let channel = channels.find((x) => x._id === id);
+            if (channel?.alertCount) {
+                alertCount += channel.alertCount;
+            }
+        }
+
+        return {
+            ...server,
+            unread: (typeof server.channels.find((x) =>
+                unreadChannels.includes(x),
+            ) !== "undefined"
+                ? alertCount > 0
+                    ? "mention"
+                    : "unread"
+                : undefined) as "mention" | "unread" | undefined,
+            alertCount,
+        };
+    });
+
+    const path = useLocation().pathname;
+    const { server: server_id } = useParams<{ server?: string }>();
+    const server = servers.find((x) => x!._id == server_id);
+
+    const { openScreen } = useIntermediate();
+
+    let homeUnread: "mention" | "unread" | undefined;
+    let alertCount = 0;
+    for (let x of channels) {
+        if (
+            ((x.channel_type === "DirectMessage" && x.active) ||
+                x.channel_type === "Group") &&
+            x.unread
+        ) {
+            homeUnread = "unread";
+            alertCount += x.alertCount ?? 0;
+        }
+    }
+
+    if (alertCount > 0) homeUnread = "mention";
+    const homeActive =
+        typeof server === "undefined" && !path.startsWith("/invite");
+
+    return (
+        <ServersBase>
+            <ServerList>
+                <ConditionalLink
+                    active={homeActive}
+                    to={lastOpened.home ? `/channel/${lastOpened.home}` : "/"}>
+                    <ServerEntry home active={homeActive}>
+                        <div
+                            onContextMenu={attachContextMenu("Status")}
+                            onClick={() =>
+                                homeActive && openContextMenu("Status")
+                            }>
+                            <Icon size={42} unread={homeUnread}>
+                                <UserIcon target={self} size={32} status />
+                            </Icon>
+                        </div>
+                        <span />
+                    </ServerEntry>
+                </ConditionalLink>
+                <LineDivider />
+                {servers.map((entry) => {
+                    const active = entry!._id === server?._id;
+                    const id = lastOpened[entry!._id];
+
+                    return (
+                        <ConditionalLink
+                            active={active}
+                            to={
+                                `/server/${entry!._id}` +
+                                (id ? `/channel/${id}` : "")
+                            }>
+                            <ServerEntry
+                                active={active}
+                                onContextMenu={attachContextMenu("Menu", {
+                                    server: entry!._id,
+                                })}>
+                                <Tooltip content={entry.name} placement="right">
+                                    <Icon size={42} unread={entry.unread}>
+                                        <ServerIcon size={32} target={entry} />
+                                    </Icon>
+                                </Tooltip>
+                                <span />
+                            </ServerEntry>
+                        </ConditionalLink>
+                    );
+                })}
+                <IconButton
+                    onClick={() =>
+                        openScreen({
+                            id: "special_input",
+                            type: "create_server",
+                        })
+                    }>
+                    <Plus size={36} />
+                </IconButton>
+                <PaintCounter small />
+            </ServerList>
+        </ServersBase>
+    );
 }
 
 export default connectState(ServerListSidebar, (state) => {
-	return {
-		unreads: state.unreads,
-		lastOpened: state.lastOpened,
-	};
+    return {
+        unreads: state.unreads,
+        lastOpened: state.lastOpened,
+    };
 });
diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx
index 38bc164..82db3e2 100644
--- a/src/components/navigation/left/ServerSidebar.tsx
+++ b/src/components/navigation/left/ServerSidebar.tsx
@@ -13,9 +13,9 @@ import { connectState } from "../../../redux/connector";
 import { Unreads } from "../../../redux/reducers/unreads";
 
 import {
-	useChannels,
-	useForceUpdate,
-	useServer,
+    useChannels,
+    useForceUpdate,
+    useServer,
 } from "../../../context/revoltjs/hooks";
 
 import CollapsibleSection from "../../common/CollapsibleSection";
@@ -27,124 +27,124 @@ import { ChannelButton } from "../items/ButtonItem";
 import ConnectionStatus from "../items/ConnectionStatus";
 
 interface Props {
-	unreads: Unreads;
+    unreads: Unreads;
 }
 
 const ServerBase = styled.div`
-	height: 100%;
-	width: 240px;
-	display: flex;
-	flex-shrink: 0;
-	flex-direction: column;
-	background: var(--secondary-background);
-
-	border-start-start-radius: 8px;
-	border-end-start-radius: 8px;
-	overflow: hidden;
+    height: 100%;
+    width: 240px;
+    display: flex;
+    flex-shrink: 0;
+    flex-direction: column;
+    background: var(--secondary-background);
+
+    border-start-start-radius: 8px;
+    border-end-start-radius: 8px;
+    overflow: hidden;
 `;
 
 const ServerList = styled.div`
-	padding: 6px;
-	flex-grow: 1;
-	overflow-y: scroll;
+    padding: 6px;
+    flex-grow: 1;
+    overflow-y: scroll;
 
-	> svg {
-		width: 100%;
-	}
+    > svg {
+        width: 100%;
+    }
 `;
 
 function ServerSidebar(props: Props) {
-	const { server: server_id, channel: channel_id } =
-		useParams<{ server?: string; channel?: string }>();
-	const ctx = useForceUpdate();
-
-	const server = useServer(server_id, ctx);
-	if (!server) return <Redirect to="/" />;
-
-	const channels = (
-		useChannels(server.channels, ctx).filter(
-			(entry) => typeof entry !== "undefined",
-		) as Readonly<Channels.TextChannel | Channels.VoiceChannel>[]
-	).map((x) => mapChannelWithUnread(x, props.unreads));
-
-	const channel = channels.find((x) => x?._id === channel_id);
-	if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
-	if (channel) useUnreads({ ...props, channel }, ctx);
-
-	useEffect(() => {
-		if (!channel_id) return;
-
-		dispatch({
-			type: "LAST_OPENED_SET",
-			parent: server_id!,
-			child: channel_id!,
-		});
-	}, [channel_id]);
-
-	let uncategorised = new Set(server.channels);
-	let elements = [];
-
-	function addChannel(id: string) {
-		const entry = channels.find((x) => x._id === id);
-		if (!entry) return;
-
-		const active = channel?._id === entry._id;
-
-		return (
-			<ConditionalLink
-				key={entry._id}
-				active={active}
-				to={`/server/${server!._id}/channel/${entry._id}`}>
-				<ChannelButton
-					channel={entry}
-					active={active}
-					alert={entry.unread}
-					compact
-				/>
-			</ConditionalLink>
-		);
-	}
-
-	if (server.categories) {
-		for (let category of server.categories) {
-			let channels = [];
-			for (let id of category.channels) {
-				uncategorised.delete(id);
-				channels.push(addChannel(id));
-			}
-
-			elements.push(
-				<CollapsibleSection
-					id={`category_${category.id}`}
-					defaultValue
-					summary={<Category text={category.title} />}>
-					{channels}
-				</CollapsibleSection>,
-			);
-		}
-	}
-
-	for (let id of Array.from(uncategorised).reverse()) {
-		elements.unshift(addChannel(id));
-	}
-
-	return (
-		<ServerBase>
-			<ServerHeader server={server} ctx={ctx} />
-			<ConnectionStatus />
-			<ServerList
-				onContextMenu={attachContextMenu("Menu", {
-					server_list: server._id,
-				})}>
-				{elements}
-			</ServerList>
-			<PaintCounter small />
-		</ServerBase>
-	);
+    const { server: server_id, channel: channel_id } =
+        useParams<{ server?: string; channel?: string }>();
+    const ctx = useForceUpdate();
+
+    const server = useServer(server_id, ctx);
+    if (!server) return <Redirect to="/" />;
+
+    const channels = (
+        useChannels(server.channels, ctx).filter(
+            (entry) => typeof entry !== "undefined",
+        ) as Readonly<Channels.TextChannel | Channels.VoiceChannel>[]
+    ).map((x) => mapChannelWithUnread(x, props.unreads));
+
+    const channel = channels.find((x) => x?._id === channel_id);
+    if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
+    if (channel) useUnreads({ ...props, channel }, ctx);
+
+    useEffect(() => {
+        if (!channel_id) return;
+
+        dispatch({
+            type: "LAST_OPENED_SET",
+            parent: server_id!,
+            child: channel_id!,
+        });
+    }, [channel_id]);
+
+    let uncategorised = new Set(server.channels);
+    let elements = [];
+
+    function addChannel(id: string) {
+        const entry = channels.find((x) => x._id === id);
+        if (!entry) return;
+
+        const active = channel?._id === entry._id;
+
+        return (
+            <ConditionalLink
+                key={entry._id}
+                active={active}
+                to={`/server/${server!._id}/channel/${entry._id}`}>
+                <ChannelButton
+                    channel={entry}
+                    active={active}
+                    alert={entry.unread}
+                    compact
+                />
+            </ConditionalLink>
+        );
+    }
+
+    if (server.categories) {
+        for (let category of server.categories) {
+            let channels = [];
+            for (let id of category.channels) {
+                uncategorised.delete(id);
+                channels.push(addChannel(id));
+            }
+
+            elements.push(
+                <CollapsibleSection
+                    id={`category_${category.id}`}
+                    defaultValue
+                    summary={<Category text={category.title} />}>
+                    {channels}
+                </CollapsibleSection>,
+            );
+        }
+    }
+
+    for (let id of Array.from(uncategorised).reverse()) {
+        elements.unshift(addChannel(id));
+    }
+
+    return (
+        <ServerBase>
+            <ServerHeader server={server} ctx={ctx} />
+            <ConnectionStatus />
+            <ServerList
+                onContextMenu={attachContextMenu("Menu", {
+                    server_list: server._id,
+                })}>
+                {elements}
+            </ServerList>
+            <PaintCounter small />
+        </ServerBase>
+    );
 }
 
 export default connectState(ServerSidebar, (state) => {
-	return {
-		unreads: state.unreads,
-	};
+    return {
+        unreads: state.unreads,
+    };
 });
diff --git a/src/components/navigation/left/common.ts b/src/components/navigation/left/common.ts
index 8916803..f8539c2 100644
--- a/src/components/navigation/left/common.ts
+++ b/src/components/navigation/left/common.ts
@@ -8,96 +8,96 @@ import { Unreads } from "../../../redux/reducers/unreads";
 import { HookContext, useForceUpdate } from "../../../context/revoltjs/hooks";
 
 type UnreadProps = {
-	channel: Channel;
-	unreads: Unreads;
+    channel: Channel;
+    unreads: Unreads;
 };
 
 export function useUnreads(
-	{ channel, unreads }: UnreadProps,
-	context?: HookContext,
+    { channel, unreads }: UnreadProps,
+    context?: HookContext,
 ) {
-	const ctx = useForceUpdate(context);
+    const ctx = useForceUpdate(context);
 
-	useLayoutEffect(() => {
-		function checkUnread(target?: Channel) {
-			if (!target) return;
-			if (target._id !== channel._id) return;
-			if (
-				target.channel_type === "SavedMessages" ||
-				target.channel_type === "VoiceChannel"
-			)
-				return;
+    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) {
-				const message =
-					typeof target.last_message === "string"
-						? target.last_message
-						: target.last_message._id;
-				if (!unread || (unread && message.localeCompare(unread) > 0)) {
-					dispatch({
-						type: "UNREADS_MARK_READ",
-						channel: channel._id,
-						message,
-					});
+            const unread = unreads[channel._id]?.last_id;
+            if (target.last_message) {
+                const message =
+                    typeof target.last_message === "string"
+                        ? target.last_message
+                        : target.last_message._id;
+                if (!unread || (unread && message.localeCompare(unread) > 0)) {
+                    dispatch({
+                        type: "UNREADS_MARK_READ",
+                        channel: channel._id,
+                        message,
+                    });
 
-					ctx.client.req(
-						"PUT",
-						`/channels/${channel._id}/ack/${message}` as "/channels/id/ack/id",
-					);
-				}
-			}
-		}
+                    ctx.client.req(
+                        "PUT",
+                        `/channels/${channel._id}/ack/${message}` as "/channels/id/ack/id",
+                    );
+                }
+            }
+        }
 
-		checkUnread(channel);
+        checkUnread(channel);
 
-		ctx.client.channels.addListener("mutation", checkUnread);
-		return () =>
-			ctx.client.channels.removeListener("mutation", checkUnread);
-	}, [channel, unreads]);
+        ctx.client.channels.addListener("mutation", checkUnread);
+        return () =>
+            ctx.client.channels.removeListener("mutation", checkUnread);
+    }, [channel, unreads]);
 }
 
 export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
-	let last_message_id;
-	if (
-		channel.channel_type === "DirectMessage" ||
-		channel.channel_type === "Group"
-	) {
-		last_message_id = channel.last_message?._id;
-	} else if (channel.channel_type === "TextChannel") {
-		last_message_id = channel.last_message;
-	} else {
-		return {
-			...channel,
-			unread: undefined,
-			alertCount: undefined,
-			timestamp: channel._id,
-		};
-	}
+    let last_message_id;
+    if (
+        channel.channel_type === "DirectMessage" ||
+        channel.channel_type === "Group"
+    ) {
+        last_message_id = channel.last_message?._id;
+    } else if (channel.channel_type === "TextChannel") {
+        last_message_id = channel.last_message;
+    } else {
+        return {
+            ...channel,
+            unread: undefined,
+            alertCount: undefined,
+            timestamp: channel._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.localeCompare(u.last_id) > 0
-			) {
-				unread = "unread";
-			}
-		} else {
-			unread = "unread";
-		}
-	}
+    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.localeCompare(u.last_id) > 0
+            ) {
+                unread = "unread";
+            }
+        } else {
+            unread = "unread";
+        }
+    }
 
-	return {
-		...channel,
-		timestamp: last_message_id ?? channel._id,
-		unread,
-		alertCount,
-	};
+    return {
+        ...channel,
+        timestamp: last_message_id ?? channel._id,
+        unread,
+        alertCount,
+    };
 }
diff --git a/src/components/navigation/right/ChannelDebugInfo.tsx b/src/components/navigation/right/ChannelDebugInfo.tsx
index 392f547..17ac06d 100644
--- a/src/components/navigation/right/ChannelDebugInfo.tsx
+++ b/src/components/navigation/right/ChannelDebugInfo.tsx
@@ -1,40 +1,40 @@
 import { useRenderState } from "../../../lib/renderer/Singleton";
 
 interface Props {
-	id: string;
+    id: string;
 }
 
 export function ChannelDebugInfo({ id }: Props) {
-	if (process.env.NODE_ENV !== "development") return null;
-	let view = useRenderState(id);
-	if (!view) return null;
+    if (process.env.NODE_ENV !== "development") return null;
+    let view = useRenderState(id);
+    if (!view) return null;
 
-	return (
-		<span style={{ display: "block", padding: "12px 10px 0 10px" }}>
-			<span
-				style={{
-					display: "block",
-					fontSize: "12px",
-					textTransform: "uppercase",
-					fontWeight: "600",
-				}}>
-				Channel Info
-			</span>
-			<p style={{ fontSize: "10px", userSelect: "text" }}>
-				State: <b>{view.type}</b> <br />
-				{view.type === "RENDER" && view.messages.length > 0 && (
-					<>
-						Start: <b>{view.messages[0]._id}</b> <br />
-						End:{" "}
-						<b>
-							{view.messages[view.messages.length - 1]._id}
-						</b>{" "}
-						<br />
-						At Top: <b>{view.atTop ? "Yes" : "No"}</b> <br />
-						At Bottom: <b>{view.atBottom ? "Yes" : "No"}</b>
-					</>
-				)}
-			</p>
-		</span>
-	);
+    return (
+        <span style={{ display: "block", padding: "12px 10px 0 10px" }}>
+            <span
+                style={{
+                    display: "block",
+                    fontSize: "12px",
+                    textTransform: "uppercase",
+                    fontWeight: "600",
+                }}>
+                Channel Info
+            </span>
+            <p style={{ fontSize: "10px", userSelect: "text" }}>
+                State: <b>{view.type}</b> <br />
+                {view.type === "RENDER" && view.messages.length > 0 && (
+                    <>
+                        Start: <b>{view.messages[0]._id}</b> <br />
+                        End:{" "}
+                        <b>
+                            {view.messages[view.messages.length - 1]._id}
+                        </b>{" "}
+                        <br />
+                        At Top: <b>{view.atTop ? "Yes" : "No"}</b> <br />
+                        At Bottom: <b>{view.atBottom ? "Yes" : "No"}</b>
+                    </>
+                )}
+            </p>
+        </span>
+    );
 }
diff --git a/src/components/navigation/right/MemberSidebar.tsx b/src/components/navigation/right/MemberSidebar.tsx
index a24fc93..1381692 100644
--- a/src/components/navigation/right/MemberSidebar.tsx
+++ b/src/components/navigation/right/MemberSidebar.tsx
@@ -8,15 +8,15 @@ import { useContext, useEffect, useState } from "preact/hooks";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import {
-	AppContext,
-	ClientStatus,
-	StatusContext,
+    AppContext,
+    ClientStatus,
+    StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
 import {
-	HookContext,
-	useChannel,
-	useForceUpdate,
-	useUsers,
+    HookContext,
+    useChannel,
+    useForceUpdate,
+    useUsers,
 } from "../../../context/revoltjs/hooks";
 
 import Category from "../../ui/Category";
@@ -28,35 +28,35 @@ import { UserButton } from "../items/ButtonItem";
 import { ChannelDebugInfo } from "./ChannelDebugInfo";
 
 interface Props {
-	ctx: HookContext;
+    ctx: HookContext;
 }
 
 export default function MemberSidebar(props: { channel?: Channels.Channel }) {
-	const ctx = useForceUpdate();
-	const { channel: cid } = useParams<{ channel: string }>();
-	const channel = props.channel ?? useChannel(cid, ctx);
+    const ctx = useForceUpdate();
+    const { channel: cid } = useParams<{ channel: string }>();
+    const channel = props.channel ?? useChannel(cid, ctx);
 
-	switch (channel?.channel_type) {
-		case "Group":
-			return <GroupMemberSidebar channel={channel} ctx={ctx} />;
-		case "TextChannel":
-			return <ServerMemberSidebar channel={channel} ctx={ctx} />;
-		default:
-			return null;
-	}
+    switch (channel?.channel_type) {
+        case "Group":
+            return <GroupMemberSidebar channel={channel} ctx={ctx} />;
+        case "TextChannel":
+            return <ServerMemberSidebar channel={channel} ctx={ctx} />;
+        default:
+            return null;
+    }
 }
 
 export function GroupMemberSidebar({
-	channel,
-	ctx,
+    channel,
+    ctx,
 }: Props & { channel: Channels.GroupChannel }) {
-	const { openScreen } = useIntermediate();
-	const users = useUsers(undefined, ctx);
-	let members = channel.recipients
-		.map((x) => users.find((y) => y?._id === x))
-		.filter((x) => typeof x !== "undefined") as User[];
+    const { openScreen } = useIntermediate();
+    const users = useUsers(undefined, ctx);
+    let members = channel.recipients
+        .map((x) => users.find((y) => y?._id === x))
+        .filter((x) => typeof x !== "undefined") as User[];
 
-	/*const voice = useContext(VoiceContext);
+    /*const voice = useContext(VoiceContext);
     const voiceActive = voice.roomId === channel._id;
 
     let voiceParticipants: User[] = [];
@@ -71,32 +71,32 @@ export function GroupMemberSidebar({
         voiceParticipants.sort((a, b) => a.username.localeCompare(b.username));
     }*/
 
-	members.sort((a, b) => {
-		// ! FIXME: should probably rewrite all this code
-		let l =
-			+(
-				(a.online && a.status?.presence !== Users.Presence.Invisible) ??
-				false
-			) | 0;
-		let r =
-			+(
-				(b.online && b.status?.presence !== Users.Presence.Invisible) ??
-				false
-			) | 0;
+    members.sort((a, b) => {
+        // ! FIXME: should probably rewrite all this code
+        let l =
+            +(
+                (a.online && a.status?.presence !== Users.Presence.Invisible) ??
+                false
+            ) | 0;
+        let r =
+            +(
+                (b.online && b.status?.presence !== Users.Presence.Invisible) ??
+                false
+            ) | 0;
 
-		let n = r - l;
-		if (n !== 0) {
-			return n;
-		}
+        let n = r - l;
+        if (n !== 0) {
+            return n;
+        }
 
-		return a.username.localeCompare(b.username);
-	});
+        return a.username.localeCompare(b.username);
+    });
 
-	return (
-		<GenericSidebarBase>
-			<GenericSidebarList>
-				<ChannelDebugInfo id={channel._id} />
-				{/*voiceActive && voiceParticipants.length !== 0 && (
+    return (
+        <GenericSidebarBase>
+            <GenericSidebarList>
+                <ChannelDebugInfo id={channel._id} />
+                {/*voiceActive && voiceParticipants.length !== 0 && (
                     <Fragment>
                         <Category
                             type="members"
@@ -121,146 +121,146 @@ export function GroupMemberSidebar({
                         )}
                     </Fragment>
                 )*/}
-				{!((members.length === 0) /*&& voiceActive*/) && (
-					<Category
-						variant="uniform"
-						text={
-							<span>
-								<Text id="app.main.categories.members" /> —{" "}
-								{channel.recipients.length}
-							</span>
-						}
-					/>
-				)}
-				{members.length === 0 && (
-					/*!voiceActive &&*/ <img src={placeholderSVG} />
-				)}
-				{members.map(
-					(user) =>
-						user && (
-							<UserButton
-								key={user._id}
-								user={user}
-								context={channel}
-								onClick={() =>
-									openScreen({
-										id: "profile",
-										user_id: user._id,
-									})
-								}
-							/>
-						),
-				)}
-			</GenericSidebarList>
-		</GenericSidebarBase>
-	);
+                {!((members.length === 0) /*&& voiceActive*/) && (
+                    <Category
+                        variant="uniform"
+                        text={
+                            <span>
+                                <Text id="app.main.categories.members" /> —{" "}
+                                {channel.recipients.length}
+                            </span>
+                        }
+                    />
+                )}
+                {members.length === 0 && (
+                    /*!voiceActive &&*/ <img src={placeholderSVG} />
+                )}
+                {members.map(
+                    (user) =>
+                        user && (
+                            <UserButton
+                                key={user._id}
+                                user={user}
+                                context={channel}
+                                onClick={() =>
+                                    openScreen({
+                                        id: "profile",
+                                        user_id: user._id,
+                                    })
+                                }
+                            />
+                        ),
+                )}
+            </GenericSidebarList>
+        </GenericSidebarBase>
+    );
 }
 
 export function ServerMemberSidebar({
-	channel,
-	ctx,
+    channel,
+    ctx,
 }: Props & { channel: Channels.TextChannel }) {
-	const [members, setMembers] = useState<Servers.Member[] | undefined>(
-		undefined,
-	);
-	const users = useUsers(members?.map((x) => x._id.user) ?? []).filter(
-		(x) => typeof x !== "undefined",
-		ctx,
-	) as Users.User[];
-	const { openScreen } = useIntermediate();
-	const status = useContext(StatusContext);
-	const client = useContext(AppContext);
+    const [members, setMembers] = useState<Servers.Member[] | undefined>(
+        undefined,
+    );
+    const users = useUsers(members?.map((x) => x._id.user) ?? []).filter(
+        (x) => typeof x !== "undefined",
+        ctx,
+    ) as Users.User[];
+    const { openScreen } = useIntermediate();
+    const status = useContext(StatusContext);
+    const client = useContext(AppContext);
 
-	useEffect(() => {
-		if (status === ClientStatus.ONLINE && typeof members === "undefined") {
-			client.servers.members
-				.fetchMembers(channel.server)
-				.then((members) => setMembers(members));
-		}
-	}, [status]);
+    useEffect(() => {
+        if (status === ClientStatus.ONLINE && typeof members === "undefined") {
+            client.servers.members
+                .fetchMembers(channel.server)
+                .then((members) => setMembers(members));
+        }
+    }, [status]);
 
-	// ! FIXME: temporary code
-	useEffect(() => {
-		function onPacket(packet: ClientboundNotification) {
-			if (!members) return;
-			if (packet.type === "ServerMemberJoin") {
-				if (packet.id !== channel.server) return;
-				setMembers([
-					...members,
-					{ _id: { server: packet.id, user: packet.user } },
-				]);
-			} else if (packet.type === "ServerMemberLeave") {
-				if (packet.id !== channel.server) return;
-				setMembers(
-					members.filter(
-						(x) =>
-							!(
-								x._id.user === packet.user &&
-								x._id.server === packet.id
-							),
-					),
-				);
-			}
-		}
+    // ! FIXME: temporary code
+    useEffect(() => {
+        function onPacket(packet: ClientboundNotification) {
+            if (!members) return;
+            if (packet.type === "ServerMemberJoin") {
+                if (packet.id !== channel.server) return;
+                setMembers([
+                    ...members,
+                    { _id: { server: packet.id, user: packet.user } },
+                ]);
+            } else if (packet.type === "ServerMemberLeave") {
+                if (packet.id !== channel.server) return;
+                setMembers(
+                    members.filter(
+                        (x) =>
+                            !(
+                                x._id.user === packet.user &&
+                                x._id.server === packet.id
+                            ),
+                    ),
+                );
+            }
+        }
 
-		client.addListener("packet", onPacket);
-		return () => client.removeListener("packet", onPacket);
-	}, [members]);
+        client.addListener("packet", onPacket);
+        return () => client.removeListener("packet", onPacket);
+    }, [members]);
 
-	// copy paste from above
-	users.sort((a, b) => {
-		// ! FIXME: should probably rewrite all this code
-		let l =
-			+(
-				(a.online && a.status?.presence !== Users.Presence.Invisible) ??
-				false
-			) | 0;
-		let r =
-			+(
-				(b.online && b.status?.presence !== Users.Presence.Invisible) ??
-				false
-			) | 0;
+    // copy paste from above
+    users.sort((a, b) => {
+        // ! FIXME: should probably rewrite all this code
+        let l =
+            +(
+                (a.online && a.status?.presence !== Users.Presence.Invisible) ??
+                false
+            ) | 0;
+        let r =
+            +(
+                (b.online && b.status?.presence !== Users.Presence.Invisible) ??
+                false
+            ) | 0;
 
-		let n = r - l;
-		if (n !== 0) {
-			return n;
-		}
+        let n = r - l;
+        if (n !== 0) {
+            return n;
+        }
 
-		return a.username.localeCompare(b.username);
-	});
+        return a.username.localeCompare(b.username);
+    });
 
-	return (
-		<GenericSidebarBase>
-			<GenericSidebarList>
-				<ChannelDebugInfo id={channel._id} />
-				<Category
-					variant="uniform"
-					text={
-						<span>
-							<Text id="app.main.categories.members" /> —{" "}
-							{users.length}
-						</span>
-					}
-				/>
-				{!members && <Preloader type="ring" />}
-				{members && users.length === 0 && <img src={placeholderSVG} />}
-				{users.map(
-					(user) =>
-						user && (
-							<UserButton
-								key={user._id}
-								user={user}
-								context={channel}
-								onClick={() =>
-									openScreen({
-										id: "profile",
-										user_id: user._id,
-									})
-								}
-							/>
-						),
-				)}
-			</GenericSidebarList>
-		</GenericSidebarBase>
-	);
+    return (
+        <GenericSidebarBase>
+            <GenericSidebarList>
+                <ChannelDebugInfo id={channel._id} />
+                <Category
+                    variant="uniform"
+                    text={
+                        <span>
+                            <Text id="app.main.categories.members" /> —{" "}
+                            {users.length}
+                        </span>
+                    }
+                />
+                {!members && <Preloader type="ring" />}
+                {members && users.length === 0 && <img src={placeholderSVG} />}
+                {users.map(
+                    (user) =>
+                        user && (
+                            <UserButton
+                                key={user._id}
+                                user={user}
+                                context={channel}
+                                onClick={() =>
+                                    openScreen({
+                                        id: "profile",
+                                        user_id: user._id,
+                                    })
+                                }
+                            />
+                        ),
+                )}
+            </GenericSidebarList>
+        </GenericSidebarBase>
+    );
 }
diff --git a/src/components/ui/Banner.tsx b/src/components/ui/Banner.tsx
index 1ff99f8..4b96d20 100644
--- a/src/components/ui/Banner.tsx
+++ b/src/components/ui/Banner.tsx
@@ -1,10 +1,10 @@
 import styled from "styled-components";
 
 export default styled.div`
-	padding: 8px;
-	font-size: 14px;
-	text-align: center;
+    padding: 8px;
+    font-size: 14px;
+    text-align: center;
 
-	color: var(--accent);
-	background: var(--primary-background);
+    color: var(--accent);
+    background: var(--primary-background);
 `;
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
index 7604da3..870e7ed 100644
--- a/src/components/ui/Button.tsx
+++ b/src/components/ui/Button.tsx
@@ -1,71 +1,71 @@
 import styled, { css } from "styled-components";
 
 interface Props {
-	readonly contrast?: boolean;
-	readonly error?: boolean;
+    readonly contrast?: boolean;
+    readonly error?: boolean;
 }
 
 export default styled.button<Props>`
-	z-index: 1;
-	padding: 8px;
-	font-size: 16px;
-	text-align: center;
-	font-family: inherit;
+    z-index: 1;
+    padding: 8px;
+    font-size: 16px;
+    text-align: center;
+    font-family: inherit;
 
-	transition: 0.2s ease opacity;
-	transition: 0.2s ease background-color;
+    transition: 0.2s ease opacity;
+    transition: 0.2s ease background-color;
 
-	background: var(--primary-background);
-	color: var(--foreground);
+    background: var(--primary-background);
+    color: var(--foreground);
 
-	border-radius: 6px;
-	cursor: pointer;
-	border: none;
+    border-radius: 6px;
+    cursor: pointer;
+    border: none;
 
-	&:hover {
-		background: var(--secondary-header);
-	}
+    &:hover {
+        background: var(--secondary-header);
+    }
 
-	&:disabled {
-		background: var(--primary-background);
-	}
+    &:disabled {
+        background: var(--primary-background);
+    }
 
-	&:active {
-		background: var(--secondary-background);
-	}
+    &:active {
+        background: var(--secondary-background);
+    }
 
-	${(props) =>
-		props.contrast &&
-		css`
-			padding: 4px 8px;
-			background: var(--secondary-header);
+    ${(props) =>
+        props.contrast &&
+        css`
+            padding: 4px 8px;
+            background: var(--secondary-header);
 
-			&:hover {
-				background: var(--primary-header);
-			}
+            &:hover {
+                background: var(--primary-header);
+            }
 
-			&:disabled {
-				background: var(--secondary-header);
-			}
+            &:disabled {
+                background: var(--secondary-header);
+            }
 
-			&:active {
-				background: var(--secondary-background);
-			}
-		`}
+            &:active {
+                background: var(--secondary-background);
+            }
+        `}
 
-	${(props) =>
-		props.error &&
-		css`
-			color: white;
-			background: var(--error);
+    ${(props) =>
+        props.error &&
+        css`
+            color: white;
+            background: var(--error);
 
-			&:hover {
-				filter: brightness(1.2);
-				background: var(--error);
-			}
+            &:hover {
+                filter: brightness(1.2);
+                background: var(--error);
+            }
 
-			&:disabled {
-				background: var(--error);
-			}
-		`}
+            &:disabled {
+                background: var(--error);
+            }
+        `}
 `;
diff --git a/src/components/ui/Category.tsx b/src/components/ui/Category.tsx
index 80d117c..914d822 100644
--- a/src/components/ui/Category.tsx
+++ b/src/components/ui/Category.tsx
@@ -4,52 +4,52 @@ import styled, { css } from "styled-components";
 import { Children } from "../../types/Preact";
 
 const CategoryBase = styled.div<Pick<Props, "variant">>`
-	font-size: 12px;
-	font-weight: 700;
-	text-transform: uppercase;
-
-	margin-top: 4px;
-	padding: 6px 0;
-	margin-bottom: 4px;
-	white-space: nowrap;
-
-	display: flex;
-	align-items: center;
-	flex-direction: row;
-	justify-content: space-between;
-
-	svg {
-		cursor: pointer;
-	}
-
-	&:first-child {
-		margin-top: 0;
-		padding-top: 0;
-	}
-
-	${(props) =>
-		props.variant === "uniform" &&
-		css`
-			padding-top: 6px;
-		`}
+    font-size: 12px;
+    font-weight: 700;
+    text-transform: uppercase;
+
+    margin-top: 4px;
+    padding: 6px 0;
+    margin-bottom: 4px;
+    white-space: nowrap;
+
+    display: flex;
+    align-items: center;
+    flex-direction: row;
+    justify-content: space-between;
+
+    svg {
+        cursor: pointer;
+    }
+
+    &:first-child {
+        margin-top: 0;
+        padding-top: 0;
+    }
+
+    ${(props) =>
+        props.variant === "uniform" &&
+        css`
+            padding-top: 6px;
+        `}
 `;
 
 type Props = Omit<
-	JSX.HTMLAttributes<HTMLDivElement>,
-	"children" | "as" | "action"
+    JSX.HTMLAttributes<HTMLDivElement>,
+    "children" | "as" | "action"
 > & {
-	text: Children;
-	action?: () => void;
-	variant?: "default" | "uniform";
+    text: Children;
+    action?: () => void;
+    variant?: "default" | "uniform";
 };
 
 export default function Category(props: Props) {
-	let { text, action, ...otherProps } = props;
-
-	return (
-		<CategoryBase {...otherProps}>
-			{text}
-			{action && <Plus size={16} onClick={action} />}
-		</CategoryBase>
-	);
+    let { text, action, ...otherProps } = props;
+
+    return (
+        <CategoryBase {...otherProps}>
+            {text}
+            {action && <Plus size={16} onClick={action} />}
+        </CategoryBase>
+    );
 }
diff --git a/src/components/ui/Checkbox.tsx b/src/components/ui/Checkbox.tsx
index 44d8011..26e531d 100644
--- a/src/components/ui/Checkbox.tsx
+++ b/src/components/ui/Checkbox.tsx
@@ -4,105 +4,105 @@ import styled, { css } from "styled-components";
 import { Children } from "../../types/Preact";
 
 const CheckboxBase = styled.label`
-	margin-top: 20px;
-	gap: 4px;
-	z-index: 1;
-	display: flex;
-	border-radius: 4px;
-	align-items: center;
+    margin-top: 20px;
+    gap: 4px;
+    z-index: 1;
+    display: flex;
+    border-radius: 4px;
+    align-items: center;
 
-	cursor: pointer;
-	font-size: 18px;
-	user-select: none;
+    cursor: pointer;
+    font-size: 18px;
+    user-select: none;
 
-	transition: 0.2s ease all;
+    transition: 0.2s ease all;
 
-	input {
-		display: none;
-	}
+    input {
+        display: none;
+    }
 
-	&:hover {
-		.check {
-			background: var(--background);
-		}
-	}
+    &:hover {
+        .check {
+            background: var(--background);
+        }
+    }
 
-	&[disabled] {
-		opacity: 0.5;
-		cursor: not-allowed;
+    &[disabled] {
+        opacity: 0.5;
+        cursor: not-allowed;
 
-		&:hover {
-			background: unset;
-		}
-	}
+        &:hover {
+            background: unset;
+        }
+    }
 `;
 
 const CheckboxContent = styled.span`
-	display: flex;
-	flex-grow: 1;
-	font-size: 1rem;
-	font-weight: 600;
-	flex-direction: column;
+    display: flex;
+    flex-grow: 1;
+    font-size: 1rem;
+    font-weight: 600;
+    flex-direction: column;
 `;
 
 const CheckboxDescription = styled.span`
-	font-size: 0.75rem;
-	font-weight: 400;
-	color: var(--secondary-foreground);
+    font-size: 0.75rem;
+    font-weight: 400;
+    color: var(--secondary-foreground);
 `;
 
 const Checkmark = styled.div<{ checked: boolean }>`
-	margin: 4px;
-	width: 24px;
-	height: 24px;
-	display: grid;
-	flex-shrink: 0;
-	border-radius: 4px;
-	place-items: center;
-	transition: 0.2s ease all;
-	background: var(--secondary-background);
+    margin: 4px;
+    width: 24px;
+    height: 24px;
+    display: grid;
+    flex-shrink: 0;
+    border-radius: 4px;
+    place-items: center;
+    transition: 0.2s ease all;
+    background: var(--secondary-background);
 
-	svg {
-		color: var(--secondary-background);
-	}
+    svg {
+        color: var(--secondary-background);
+    }
 
-	${(props) =>
-		props.checked &&
-		css`
-			background: var(--accent) !important;
-		`}
+    ${(props) =>
+        props.checked &&
+        css`
+            background: var(--accent) !important;
+        `}
 `;
 
 export interface CheckboxProps {
-	checked: boolean;
-	disabled?: boolean;
-	className?: string;
-	children: Children;
-	description?: Children;
-	onChange: (state: boolean) => void;
+    checked: boolean;
+    disabled?: boolean;
+    className?: string;
+    children: Children;
+    description?: Children;
+    onChange: (state: boolean) => void;
 }
 
 export default function Checkbox(props: CheckboxProps) {
-	return (
-		<CheckboxBase disabled={props.disabled} className={props.className}>
-			<CheckboxContent>
-				<span>{props.children}</span>
-				{props.description && (
-					<CheckboxDescription>
-						{props.description}
-					</CheckboxDescription>
-				)}
-			</CheckboxContent>
-			<input
-				type="checkbox"
-				checked={props.checked}
-				onChange={() =>
-					!props.disabled && props.onChange(!props.checked)
-				}
-			/>
-			<Checkmark checked={props.checked} className="check">
-				<Check size={20} />
-			</Checkmark>
-		</CheckboxBase>
-	);
+    return (
+        <CheckboxBase disabled={props.disabled} className={props.className}>
+            <CheckboxContent>
+                <span>{props.children}</span>
+                {props.description && (
+                    <CheckboxDescription>
+                        {props.description}
+                    </CheckboxDescription>
+                )}
+            </CheckboxContent>
+            <input
+                type="checkbox"
+                checked={props.checked}
+                onChange={() =>
+                    !props.disabled && props.onChange(!props.checked)
+                }
+            />
+            <Checkmark checked={props.checked} className="check">
+                <Check size={20} />
+            </Checkmark>
+        </CheckboxBase>
+    );
 }
diff --git a/src/components/ui/ColourSwatches.tsx b/src/components/ui/ColourSwatches.tsx
index b0bd781..d9e0d66 100644
--- a/src/components/ui/ColourSwatches.tsx
+++ b/src/components/ui/ColourSwatches.tsx
@@ -5,123 +5,123 @@ import styled, { css } from "styled-components";
 import { useRef } from "preact/hooks";
 
 interface Props {
-	value: string;
-	onChange: (value: string) => void;
+    value: string;
+    onChange: (value: string) => void;
 }
 
 const presets = [
-	[
-		"#7B68EE",
-		"#3498DB",
-		"#1ABC9C",
-		"#F1C40F",
-		"#FF7F50",
-		"#FD6671",
-		"#E91E63",
-		"#D468EE",
-	],
-	[
-		"#594CAD",
-		"#206694",
-		"#11806A",
-		"#C27C0E",
-		"#CD5B45",
-		"#FF424F",
-		"#AD1457",
-		"#954AA8",
-	],
+    [
+        "#7B68EE",
+        "#3498DB",
+        "#1ABC9C",
+        "#F1C40F",
+        "#FF7F50",
+        "#FD6671",
+        "#E91E63",
+        "#D468EE",
+    ],
+    [
+        "#594CAD",
+        "#206694",
+        "#11806A",
+        "#C27C0E",
+        "#CD5B45",
+        "#FF424F",
+        "#AD1457",
+        "#954AA8",
+    ],
 ];
 
 const SwatchesBase = styled.div`
-	gap: 8px;
-	display: flex;
+    gap: 8px;
+    display: flex;
 
-	input {
-		opacity: 0;
-		margin-top: 44px;
-		position: absolute;
-		pointer-events: none;
-	}
+    input {
+        opacity: 0;
+        margin-top: 44px;
+        position: absolute;
+        pointer-events: none;
+    }
 `;
 
 const Swatch = styled.div<{ type: "small" | "large"; colour: string }>`
-	flex-shrink: 0;
-	cursor: pointer;
-	border-radius: 4px;
-	background-color: ${(props) => props.colour};
+    flex-shrink: 0;
+    cursor: pointer;
+    border-radius: 4px;
+    background-color: ${(props) => props.colour};
 
-	display: grid;
-	place-items: center;
+    display: grid;
+    place-items: center;
 
-	&:hover {
-		border: 3px solid var(--foreground);
-		transition: border ease-in-out 0.07s;
-	}
+    &:hover {
+        border: 3px solid var(--foreground);
+        transition: border ease-in-out 0.07s;
+    }
 
-	svg {
-		color: white;
-	}
+    svg {
+        color: white;
+    }
 
-	${(props) =>
-		props.type === "small"
-			? css`
-					width: 30px;
-					height: 30px;
+    ${(props) =>
+        props.type === "small"
+            ? css`
+                  width: 30px;
+                  height: 30px;
 
-					svg {
-						/*stroke-width: 2;*/
-					}
-			  `
-			: css`
-					width: 68px;
-					height: 68px;
-			  `}
+                  svg {
+                      /*stroke-width: 2;*/
+                  }
+              `
+            : css`
+                  width: 68px;
+                  height: 68px;
+              `}
 `;
 
 const Rows = styled.div`
-	gap: 8px;
-	display: flex;
-	flex-direction: column;
+    gap: 8px;
+    display: flex;
+    flex-direction: column;
 
-	> div {
-		gap: 8px;
-		display: flex;
-		flex-direction: row;
-	}
+    > div {
+        gap: 8px;
+        display: flex;
+        flex-direction: row;
+    }
 `;
 
 export default function ColourSwatches({ value, onChange }: Props) {
-	const ref = useRef<HTMLInputElement>();
+    const ref = useRef<HTMLInputElement>();
 
-	return (
-		<SwatchesBase>
-			<Swatch
-				colour={value}
-				type="large"
-				onClick={() => ref.current.click()}>
-				<Palette size={32} />
-			</Swatch>
-			<input
-				type="color"
-				value={value}
-				ref={ref}
-				onChange={(ev) => onChange(ev.currentTarget.value)}
-			/>
-			<Rows>
-				{presets.map((row, i) => (
-					<div key={i}>
-						{row.map((swatch, i) => (
-							<Swatch
-								colour={swatch}
-								type="small"
-								key={i}
-								onClick={() => onChange(swatch)}>
-								{swatch === value && <Check size={18} />}
-							</Swatch>
-						))}
-					</div>
-				))}
-			</Rows>
-		</SwatchesBase>
-	);
+    return (
+        <SwatchesBase>
+            <Swatch
+                colour={value}
+                type="large"
+                onClick={() => ref.current.click()}>
+                <Palette size={32} />
+            </Swatch>
+            <input
+                type="color"
+                value={value}
+                ref={ref}
+                onChange={(ev) => onChange(ev.currentTarget.value)}
+            />
+            <Rows>
+                {presets.map((row, i) => (
+                    <div key={i}>
+                        {row.map((swatch, i) => (
+                            <Swatch
+                                colour={swatch}
+                                type="small"
+                                key={i}
+                                onClick={() => onChange(swatch)}>
+                                {swatch === value && <Check size={18} />}
+                            </Swatch>
+                        ))}
+                    </div>
+                ))}
+            </Rows>
+        </SwatchesBase>
+    );
 }
diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx
index 5591124..a02c1f4 100644
--- a/src/components/ui/ComboBox.tsx
+++ b/src/components/ui/ComboBox.tsx
@@ -1,20 +1,20 @@
 import styled from "styled-components";
 
 export default styled.select`
-	padding: 8px;
-	border-radius: 6px;
-	font-family: inherit;
-	color: var(--secondary-foreground);
-	background: var(--secondary-background);
-	font-size: 0.875rem;
-	border: none;
-	outline: 2px solid transparent;
-	transition: outline-color 0.2s ease-in-out;
-	transition: box-shadow 0.3s;
-	cursor: pointer;
-	width: 100%;
+    padding: 8px;
+    border-radius: 6px;
+    font-family: inherit;
+    color: var(--secondary-foreground);
+    background: var(--secondary-background);
+    font-size: 0.875rem;
+    border: none;
+    outline: 2px solid transparent;
+    transition: outline-color 0.2s ease-in-out;
+    transition: box-shadow 0.3s;
+    cursor: pointer;
+    width: 100%;
 
-	&:focus {
-		box-shadow: 0 0 0 2pt var(--accent);
-	}
+    &:focus {
+        box-shadow: 0 0 0 2pt var(--accent);
+    }
 `;
diff --git a/src/components/ui/DateDivider.tsx b/src/components/ui/DateDivider.tsx
index 4f2a9dd..4e32c86 100644
--- a/src/components/ui/DateDivider.tsx
+++ b/src/components/ui/DateDivider.tsx
@@ -2,47 +2,47 @@ import dayjs from "dayjs";
 import styled, { css } from "styled-components";
 
 const Base = styled.div<{ unread?: boolean }>`
-	height: 0;
-	display: flex;
-	user-select: none;
-	align-items: center;
-	margin: 17px 12px 5px;
-	border-top: thin solid var(--tertiary-foreground);
+    height: 0;
+    display: flex;
+    user-select: none;
+    align-items: center;
+    margin: 17px 12px 5px;
+    border-top: thin solid var(--tertiary-foreground);
 
-	time {
-		margin-top: -2px;
-		font-size: 0.6875rem;
-		line-height: 0.6875rem;
-		padding: 2px 5px 2px 0;
-		color: var(--tertiary-foreground);
-		background: var(--primary-background);
-	}
+    time {
+        margin-top: -2px;
+        font-size: 0.6875rem;
+        line-height: 0.6875rem;
+        padding: 2px 5px 2px 0;
+        color: var(--tertiary-foreground);
+        background: var(--primary-background);
+    }
 
-	${(props) =>
-		props.unread &&
-		css`
-			border-top: thin solid var(--accent);
-		`}
+    ${(props) =>
+        props.unread &&
+        css`
+            border-top: thin solid var(--accent);
+        `}
 `;
 
 const Unread = styled.div`
-	background: var(--accent);
-	color: white;
-	padding: 5px 8px;
-	border-radius: 60px;
-	font-weight: 600;
+    background: var(--accent);
+    color: white;
+    padding: 5px 8px;
+    border-radius: 60px;
+    font-weight: 600;
 `;
 
 interface Props {
-	date: Date;
-	unread?: boolean;
+    date: Date;
+    unread?: boolean;
 }
 
 export default function DateDivider(props: Props) {
-	return (
-		<Base unread={props.unread}>
-			{props.unread && <Unread>NEW</Unread>}
-			<time>{dayjs(props.date).format("LL")}</time>
-		</Base>
-	);
+    return (
+        <Base unread={props.unread}>
+            {props.unread && <Unread>NEW</Unread>}
+            <time>{dayjs(props.date).format("LL")}</time>
+        </Base>
+    );
 }
diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx
index 58ed693..5696f29 100644
--- a/src/components/ui/Details.tsx
+++ b/src/components/ui/Details.tsx
@@ -1,74 +1,74 @@
 import styled, { css } from "styled-components";
 
 export default styled.details<{ sticky?: boolean; large?: boolean }>`
-	summary {
-		${(props) =>
-			props.sticky &&
-			css`
-				top: -1px;
-				z-index: 10;
-				position: sticky;
-			`}
+    summary {
+        ${(props) =>
+            props.sticky &&
+            css`
+                top: -1px;
+                z-index: 10;
+                position: sticky;
+            `}
 
-		${(props) =>
-			props.large &&
-			css`
-				/*padding: 5px 0;*/
-				background: var(--primary-background);
-				color: var(--secondary-foreground);
+        ${(props) =>
+            props.large &&
+            css`
+                /*padding: 5px 0;*/
+                background: var(--primary-background);
+                color: var(--secondary-foreground);
 
-				.padding {
-					/*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/
-					display: flex;
-					align-items: center;
-					padding: 5px 0;
-					margin: 0.8em 0px 0.4em;
-					cursor: pointer;
-				}
-			`}
+                .padding {
+                    /*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/
+                    display: flex;
+                    align-items: center;
+                    padding: 5px 0;
+                    margin: 0.8em 0px 0.4em;
+                    cursor: pointer;
+                }
+            `}
 
         outline: none;
-		cursor: pointer;
-		list-style: none;
-		align-items: center;
-		transition: 0.2s opacity;
+        cursor: pointer;
+        list-style: none;
+        align-items: center;
+        transition: 0.2s opacity;
 
-		font-size: 12px;
-		font-weight: 600;
-		text-transform: uppercase;
+        font-size: 12px;
+        font-weight: 600;
+        text-transform: uppercase;
 
-		&::marker,
-		&::-webkit-details-marker {
-			display: none;
-		}
+        &::marker,
+        &::-webkit-details-marker {
+            display: none;
+        }
 
-		.title {
-			flex-grow: 1;
-			margin-top: 1px;
-			text-overflow: ellipsis;
-			overflow: hidden;
-			white-space: nowrap;
-		}
+        .title {
+            flex-grow: 1;
+            margin-top: 1px;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: nowrap;
+        }
 
-		.padding {
-			display: flex;
-			align-items: center;
+        .padding {
+            display: flex;
+            align-items: center;
 
-			> svg {
-				flex-shrink: 0;
-				margin-inline-end: 4px;
-				transition: 0.2s ease transform;
-			}
-		}
-	}
+            > svg {
+                flex-shrink: 0;
+                margin-inline-end: 4px;
+                transition: 0.2s ease transform;
+            }
+        }
+    }
 
-	&:not([open]) {
-		summary {
-			opacity: 0.7;
-		}
+    &:not([open]) {
+        summary {
+            opacity: 0.7;
+        }
 
-		summary svg {
-			transform: rotateZ(-90deg);
-		}
-	}
+        summary svg {
+            transform: rotateZ(-90deg);
+        }
+    }
 `;
diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx
index e935b80..cbeaf59 100644
--- a/src/components/ui/Header.tsx
+++ b/src/components/ui/Header.tsx
@@ -1,57 +1,57 @@
 import styled, { css } from "styled-components";
 
 interface Props {
-	borders?: boolean;
-	background?: boolean;
-	placement: "primary" | "secondary";
+    borders?: boolean;
+    background?: boolean;
+    placement: "primary" | "secondary";
 }
 
 export default styled.div<Props>`
-	gap: 6px;
-	height: 48px;
-	flex: 0 auto;
-	display: flex;
-	flex-shrink: 0;
-	padding: 0 16px;
-	font-weight: 600;
-	user-select: none;
-	align-items: center;
-
-	background-size: cover !important;
-	background-position: center !important;
-	background-color: var(--primary-header);
-
-	svg {
-		flex-shrink: 0;
-	}
-
-	/*@media only screen and (max-width: 768px) {
+    gap: 6px;
+    height: 48px;
+    flex: 0 auto;
+    display: flex;
+    flex-shrink: 0;
+    padding: 0 16px;
+    font-weight: 600;
+    user-select: none;
+    align-items: center;
+
+    background-size: cover !important;
+    background-position: center !important;
+    background-color: var(--primary-header);
+
+    svg {
+        flex-shrink: 0;
+    }
+
+    /*@media only screen and (max-width: 768px) {
         padding: 0 12px;
     }*/
 
-	@media (pointer: coarse) {
-		height: 56px;
-	}
+    @media (pointer: coarse) {
+        height: 56px;
+    }
 
-	${(props) =>
-		props.background &&
-		css`
-			height: 120px !important;
-			align-items: flex-end;
+    ${(props) =>
+        props.background &&
+        css`
+            height: 120px !important;
+            align-items: flex-end;
 
-			text-shadow: 0px 0px 1px black;
-		`}
+            text-shadow: 0px 0px 1px black;
+        `}
 
-	${(props) =>
-		props.placement === "secondary" &&
-		css`
-			background-color: var(--secondary-header);
-			padding: 14px;
-		`}
+    ${(props) =>
+        props.placement === "secondary" &&
+        css`
+            background-color: var(--secondary-header);
+            padding: 14px;
+        `}
 
     ${(props) =>
-		props.borders &&
-		css`
-			border-start-start-radius: 8px;
-		`}
+        props.borders &&
+        css`
+            border-start-start-radius: 8px;
+        `}
 `;
diff --git a/src/components/ui/IconButton.tsx b/src/components/ui/IconButton.tsx
index 3044ec5..207332a 100644
--- a/src/components/ui/IconButton.tsx
+++ b/src/components/ui/IconButton.tsx
@@ -1,46 +1,46 @@
 import styled, { css } from "styled-components";
 
 interface Props {
-	type?: "default" | "circle";
+    type?: "default" | "circle";
 }
 
 const normal = `var(--secondary-foreground)`;
 const hover = `var(--foreground)`;
 
 export default styled.div<Props>`
-	z-index: 1;
-	display: grid;
-	cursor: pointer;
-	place-items: center;
-	transition: 0.1s ease background-color;
-
-	fill: ${normal};
-	color: ${normal};
-	/*stroke: ${normal};*/
-
-	a {
-		color: ${normal};
-	}
-
-	&:hover {
-		fill: ${hover};
-		color: ${hover};
-		/*stroke: ${hover};*/
-
-		a {
-			color: ${hover};
-		}
-	}
-
-	${(props) =>
-		props.type === "circle" &&
-		css`
-			padding: 4px;
-			border-radius: 50%;
-			background-color: var(--secondary-header);
-
-			&:hover {
-				background-color: var(--primary-header);
-			}
-		`}
+    z-index: 1;
+    display: grid;
+    cursor: pointer;
+    place-items: center;
+    transition: 0.1s ease background-color;
+
+    fill: ${normal};
+    color: ${normal};
+    /*stroke: ${normal};*/
+
+    a {
+        color: ${normal};
+    }
+
+    &:hover {
+        fill: ${hover};
+        color: ${hover};
+        /*stroke: ${hover};*/
+
+        a {
+            color: ${hover};
+        }
+    }
+
+    ${(props) =>
+        props.type === "circle" &&
+        css`
+            padding: 4px;
+            border-radius: 50%;
+            background-color: var(--secondary-header);
+
+            &:hover {
+                background-color: var(--primary-header);
+            }
+        `}
 `;
diff --git a/src/components/ui/InputBox.tsx b/src/components/ui/InputBox.tsx
index b6dcbb8..a791edd 100644
--- a/src/components/ui/InputBox.tsx
+++ b/src/components/ui/InputBox.tsx
@@ -1,39 +1,39 @@
 import styled, { css } from "styled-components";
 
 interface Props {
-	readonly contrast?: boolean;
+    readonly contrast?: boolean;
 }
 
 export default styled.input<Props>`
-	z-index: 1;
-	padding: 8px 16px;
-	border-radius: 6px;
-
-	font-family: inherit;
-	color: var(--foreground);
-	background: var(--primary-background);
-	transition: 0.2s ease background-color;
-
-	border: none;
-	outline: 2px solid transparent;
-	transition: outline-color 0.2s ease-in-out;
-
-	&:hover {
-		background: var(--secondary-background);
-	}
-
-	&:focus {
-		outline: 2px solid var(--accent);
-	}
-
-	${(props) =>
-		props.contrast &&
-		css`
-			color: var(--secondary-foreground);
-			background: var(--secondary-background);
-
-			&:hover {
-				background: var(--hover);
-			}
-		`}
+    z-index: 1;
+    padding: 8px 16px;
+    border-radius: 6px;
+
+    font-family: inherit;
+    color: var(--foreground);
+    background: var(--primary-background);
+    transition: 0.2s ease background-color;
+
+    border: none;
+    outline: 2px solid transparent;
+    transition: outline-color 0.2s ease-in-out;
+
+    &:hover {
+        background: var(--secondary-background);
+    }
+
+    &:focus {
+        outline: 2px solid var(--accent);
+    }
+
+    ${(props) =>
+        props.contrast &&
+        css`
+            color: var(--secondary-foreground);
+            background: var(--secondary-background);
+
+            &:hover {
+                background: var(--hover);
+            }
+        `}
 `;
diff --git a/src/components/ui/LineDivider.tsx b/src/components/ui/LineDivider.tsx
index 0ffd1e7..58a9c7a 100644
--- a/src/components/ui/LineDivider.tsx
+++ b/src/components/ui/LineDivider.tsx
@@ -1,9 +1,9 @@
 import styled from "styled-components";
 
 export default styled.div`
-	height: 0px;
-	opacity: 0.6;
-	flex-shrink: 0;
-	margin: 8px 10px;
-	border-top: 1px solid var(--tertiary-foreground);
+    height: 0px;
+    opacity: 0.6;
+    flex-shrink: 0;
+    margin: 8px 10px;
+    border-top: 1px solid var(--tertiary-foreground);
 `;
diff --git a/src/components/ui/Masks.tsx b/src/components/ui/Masks.tsx
index d8cadc7..8b6939c 100644
--- a/src/components/ui/Masks.tsx
+++ b/src/components/ui/Masks.tsx
@@ -1,22 +1,22 @@
 // This file must be imported and used at least once for SVG masks.
 
 export default function Masks() {
-	return (
-		<svg width={0} height={0} style={{ position: "fixed" }}>
-			<defs>
-				<mask id="server">
-					<rect x="0" y="0" width="32" height="32" fill="white" />
-					<circle cx="27" cy="5" r="7" fill={"black"} />
-				</mask>
-				<mask id="user">
-					<rect x="0" y="0" width="32" height="32" fill="white" />
-					<circle cx="27" cy="27" r="7" fill={"black"} />
-				</mask>
-				<mask id="overlap">
-					<rect x="0" y="0" width="32" height="32" fill="white" />
-					<circle cx="32" cy="16" r="18" fill={"black"} />
-				</mask>
-			</defs>
-		</svg>
-	);
+    return (
+        <svg width={0} height={0} style={{ position: "fixed" }}>
+            <defs>
+                <mask id="server">
+                    <rect x="0" y="0" width="32" height="32" fill="white" />
+                    <circle cx="27" cy="5" r="7" fill={"black"} />
+                </mask>
+                <mask id="user">
+                    <rect x="0" y="0" width="32" height="32" fill="white" />
+                    <circle cx="27" cy="27" r="7" fill={"black"} />
+                </mask>
+                <mask id="overlap">
+                    <rect x="0" y="0" width="32" height="32" fill="white" />
+                    <circle cx="32" cy="16" r="18" fill={"black"} />
+                </mask>
+            </defs>
+        </svg>
+    );
 }
diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx
index 57a43bf..15f47e8 100644
--- a/src/components/ui/Modal.tsx
+++ b/src/components/ui/Modal.tsx
@@ -19,181 +19,181 @@ const zoomIn = keyframes`
 `;
 
 const ModalBase = styled.div`
-	top: 0;
-	left: 0;
-	width: 100%;
-	height: 100%;
-	z-index: 9999;
-	position: fixed;
-	max-height: 100%;
-	user-select: none;
-
-	animation-name: ${open};
-	animation-duration: 0.2s;
-
-	display: grid;
-	overflow-y: auto;
-	place-items: center;
-
-	color: var(--foreground);
-	background: rgba(0, 0, 0, 0.8);
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 9999;
+    position: fixed;
+    max-height: 100%;
+    user-select: none;
+
+    animation-name: ${open};
+    animation-duration: 0.2s;
+
+    display: grid;
+    overflow-y: auto;
+    place-items: center;
+
+    color: var(--foreground);
+    background: rgba(0, 0, 0, 0.8);
 `;
 
 const ModalContainer = styled.div`
-	overflow: hidden;
-	border-radius: 8px;
-	max-width: calc(100vw - 20px);
+    overflow: hidden;
+    border-radius: 8px;
+    max-width: calc(100vw - 20px);
 
-	animation-name: ${zoomIn};
-	animation-duration: 0.25s;
-	animation-timing-function: cubic-bezier(0.3, 0.3, 0.18, 1.1);
+    animation-name: ${zoomIn};
+    animation-duration: 0.25s;
+    animation-timing-function: cubic-bezier(0.3, 0.3, 0.18, 1.1);
 `;
 
 const ModalContent = styled.div<
-	{ [key in "attachment" | "noBackground" | "border" | "padding"]?: boolean }
+    { [key in "attachment" | "noBackground" | "border" | "padding"]?: boolean }
 >`
-	border-radius: 8px;
-	text-overflow: ellipsis;
-
-	h3 {
-		margin-top: 0;
-	}
-
-	form {
-		display: flex;
-		flex-direction: column;
-	}
-
-	${(props) =>
-		!props.noBackground &&
-		css`
-			background: var(--secondary-header);
-		`}
-
-	${(props) =>
-		props.padding &&
-		css`
-			padding: 1.5em;
-		`}
+    border-radius: 8px;
+    text-overflow: ellipsis;
+
+    h3 {
+        margin-top: 0;
+    }
+
+    form {
+        display: flex;
+        flex-direction: column;
+    }
+
+    ${(props) =>
+        !props.noBackground &&
+        css`
+            background: var(--secondary-header);
+        `}
+
+    ${(props) =>
+        props.padding &&
+        css`
+            padding: 1.5em;
+        `}
 
     ${(props) =>
-		props.attachment &&
-		css`
-			border-radius: 8px 8px 0 0;
-		`}
+        props.attachment &&
+        css`
+            border-radius: 8px 8px 0 0;
+        `}
 
     ${(props) =>
-		props.border &&
-		css`
-			border-radius: 10px;
-			border: 2px solid var(--secondary-background);
-		`}
+        props.border &&
+        css`
+            border-radius: 10px;
+            border: 2px solid var(--secondary-background);
+        `}
 `;
 
 const ModalActions = styled.div`
-	gap: 8px;
-	display: flex;
-	flex-direction: row-reverse;
+    gap: 8px;
+    display: flex;
+    flex-direction: row-reverse;
 
-	padding: 1em 1.5em;
-	border-radius: 0 0 8px 8px;
-	background: var(--secondary-background);
+    padding: 1em 1.5em;
+    border-radius: 0 0 8px 8px;
+    background: var(--secondary-background);
 `;
 
 export interface Action {
-	text: Children;
-	onClick: () => void;
-	confirmation?: boolean;
-	contrast?: boolean;
-	error?: boolean;
+    text: Children;
+    onClick: () => void;
+    confirmation?: boolean;
+    contrast?: boolean;
+    error?: boolean;
 }
 
 interface Props {
-	children?: Children;
-	title?: Children;
-
-	disallowClosing?: boolean;
-	noBackground?: boolean;
-	dontModal?: boolean;
-	padding?: boolean;
-
-	onClose: () => void;
-	actions?: Action[];
-	disabled?: boolean;
-	border?: boolean;
-	visible: boolean;
+    children?: Children;
+    title?: Children;
+
+    disallowClosing?: boolean;
+    noBackground?: boolean;
+    dontModal?: boolean;
+    padding?: boolean;
+
+    onClose: () => void;
+    actions?: Action[];
+    disabled?: boolean;
+    border?: boolean;
+    visible: boolean;
 }
 
 export default function Modal(props: Props) {
-	if (!props.visible) return null;
-
-	let content = (
-		<ModalContent
-			attachment={!!props.actions}
-			noBackground={props.noBackground}
-			border={props.border}
-			padding={props.padding ?? !props.dontModal}>
-			{props.title && <h3>{props.title}</h3>}
-			{props.children}
-		</ModalContent>
-	);
-
-	if (props.dontModal) {
-		return content;
-	}
-
-	useEffect(() => {
-		if (props.disallowClosing) return;
-
-		function keyDown(e: KeyboardEvent) {
-			if (e.key === "Escape") {
-				props.onClose();
-			}
-		}
-
-		document.body.addEventListener("keydown", keyDown);
-		return () => document.body.removeEventListener("keydown", keyDown);
-	}, [props.disallowClosing, props.onClose]);
-
-	let confirmationAction = props.actions?.find(
-		(action) => action.confirmation,
-	);
-	useEffect(() => {
-		if (!confirmationAction) return;
-
-		// ! FIXME: this may be done better if we
-		// ! can focus the button although that
-		// ! doesn't seem to work...
-		function keyDown(e: KeyboardEvent) {
-			if (e.key === "Enter") {
-				confirmationAction!.onClick();
-			}
-		}
-
-		document.body.addEventListener("keydown", keyDown);
-		return () => document.body.removeEventListener("keydown", keyDown);
-	}, [confirmationAction]);
-
-	return createPortal(
-		<ModalBase
-			onClick={(!props.disallowClosing && props.onClose) || undefined}>
-			<ModalContainer onClick={(e) => (e.cancelBubble = true)}>
-				{content}
-				{props.actions && (
-					<ModalActions>
-						{props.actions.map((x) => (
-							<Button
-								contrast={x.contrast ?? true}
-								error={x.error ?? false}
-								onClick={x.onClick}
-								disabled={props.disabled}>
-								{x.text}
-							</Button>
-						))}
-					</ModalActions>
-				)}
-			</ModalContainer>
-		</ModalBase>,
-		document.body,
-	);
+    if (!props.visible) return null;
+
+    let content = (
+        <ModalContent
+            attachment={!!props.actions}
+            noBackground={props.noBackground}
+            border={props.border}
+            padding={props.padding ?? !props.dontModal}>
+            {props.title && <h3>{props.title}</h3>}
+            {props.children}
+        </ModalContent>
+    );
+
+    if (props.dontModal) {
+        return content;
+    }
+
+    useEffect(() => {
+        if (props.disallowClosing) return;
+
+        function keyDown(e: KeyboardEvent) {
+            if (e.key === "Escape") {
+                props.onClose();
+            }
+        }
+
+        document.body.addEventListener("keydown", keyDown);
+        return () => document.body.removeEventListener("keydown", keyDown);
+    }, [props.disallowClosing, props.onClose]);
+
+    let confirmationAction = props.actions?.find(
+        (action) => action.confirmation,
+    );
+    useEffect(() => {
+        if (!confirmationAction) return;
+
+        // ! FIXME: this may be done better if we
+        // ! can focus the button although that
+        // ! doesn't seem to work...
+        function keyDown(e: KeyboardEvent) {
+            if (e.key === "Enter") {
+                confirmationAction!.onClick();
+            }
+        }
+
+        document.body.addEventListener("keydown", keyDown);
+        return () => document.body.removeEventListener("keydown", keyDown);
+    }, [confirmationAction]);
+
+    return createPortal(
+        <ModalBase
+            onClick={(!props.disallowClosing && props.onClose) || undefined}>
+            <ModalContainer onClick={(e) => (e.cancelBubble = true)}>
+                {content}
+                {props.actions && (
+                    <ModalActions>
+                        {props.actions.map((x) => (
+                            <Button
+                                contrast={x.contrast ?? true}
+                                error={x.error ?? false}
+                                onClick={x.onClick}
+                                disabled={props.disabled}>
+                                {x.text}
+                            </Button>
+                        ))}
+                    </ModalActions>
+                )}
+            </ModalContainer>
+        </ModalBase>,
+        document.body,
+    );
 }
diff --git a/src/components/ui/Overline.tsx b/src/components/ui/Overline.tsx
index 8dc0b54..cf30bbd 100644
--- a/src/components/ui/Overline.tsx
+++ b/src/components/ui/Overline.tsx
@@ -5,60 +5,60 @@ import { Text } from "preact-i18n";
 import { Children } from "../../types/Preact";
 
 type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, "children" | "as"> & {
-	error?: string;
-	block?: boolean;
-	spaced?: boolean;
-	children?: Children;
-	type?: "default" | "subtle" | "error";
+    error?: string;
+    block?: boolean;
+    spaced?: boolean;
+    children?: Children;
+    type?: "default" | "subtle" | "error";
 };
 
 const OverlineBase = styled.div<Omit<Props, "children" | "error">>`
-	display: inline;
-	margin: 0.4em 0;
+    display: inline;
+    margin: 0.4em 0;
 
-	${(props) =>
-		props.spaced &&
-		css`
-			margin-top: 0.8em;
-		`}
+    ${(props) =>
+        props.spaced &&
+        css`
+            margin-top: 0.8em;
+        `}
 
-	font-size: 14px;
-	font-weight: 600;
-	color: var(--foreground);
-	text-transform: uppercase;
+    font-size: 14px;
+    font-weight: 600;
+    color: var(--foreground);
+    text-transform: uppercase;
 
-	${(props) =>
-		props.type === "subtle" &&
-		css`
-			font-size: 12px;
-			color: var(--secondary-foreground);
-		`}
+    ${(props) =>
+        props.type === "subtle" &&
+        css`
+            font-size: 12px;
+            color: var(--secondary-foreground);
+        `}
 
-	${(props) =>
-		props.type === "error" &&
-		css`
-			font-size: 12px;
-			font-weight: 400;
-			color: var(--error);
-		`}
+    ${(props) =>
+        props.type === "error" &&
+        css`
+            font-size: 12px;
+            font-weight: 400;
+            color: var(--error);
+        `}
 
     ${(props) =>
-		props.block &&
-		css`
-			display: block;
-		`}
+        props.block &&
+        css`
+            display: block;
+        `}
 `;
 
 export default function Overline(props: Props) {
-	return (
-		<OverlineBase {...props}>
-			{props.children}
-			{props.children && props.error && <> &middot; </>}
-			{props.error && (
-				<Overline type="error">
-					<Text id={`error.${props.error}`}>{props.error}</Text>
-				</Overline>
-			)}
-		</OverlineBase>
-	);
+    return (
+        <OverlineBase {...props}>
+            {props.children}
+            {props.children && props.error && <> &middot; </>}
+            {props.error && (
+                <Overline type="error">
+                    <Text id={`error.${props.error}`}>{props.error}</Text>
+                </Overline>
+            )}
+        </OverlineBase>
+    );
 }
diff --git a/src/components/ui/Preloader.tsx b/src/components/ui/Preloader.tsx
index a9e70ab..00f3db3 100644
--- a/src/components/ui/Preloader.tsx
+++ b/src/components/ui/Preloader.tsx
@@ -21,83 +21,83 @@ const prRing = keyframes`
 `;
 
 const PreloaderBase = styled.div`
-	width: 100%;
-	height: 100%;
-
-	display: grid;
-	place-items: center;
-
-	.spinner {
-		width: 58px;
-		display: flex;
-		text-align: center;
-		margin: 100px auto 0;
-		justify-content: space-between;
-	}
-
-	.spinner > div {
-		width: 14px;
-		height: 14px;
-		background-color: var(--tertiary-foreground);
-
-		border-radius: 100%;
-		display: inline-block;
-		animation: ${skSpinner} 1.4s infinite ease-in-out both;
-	}
-
-	.spinner div:nth-child(1) {
-		animation-delay: -0.32s;
-	}
-
-	.spinner div:nth-child(2) {
-		animation-delay: -0.16s;
-	}
-
-	.ring {
-		display: inline-block;
-		position: relative;
-		width: 48px;
-		height: 52px;
-	}
-
-	.ring div {
-		width: 32px;
-		margin: 8px;
-		height: 32px;
-		display: block;
-		position: absolute;
-		border-radius: 50%;
-		box-sizing: border-box;
-		border: 2px solid #fff;
-		animation: ${prRing} 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
-		border-color: #fff transparent transparent transparent;
-	}
-
-	.ring div:nth-child(1) {
-		animation-delay: -0.45s;
-	}
-
-	.ring div:nth-child(2) {
-		animation-delay: -0.3s;
-	}
-
-	.ring div:nth-child(3) {
-		animation-delay: -0.15s;
-	}
+    width: 100%;
+    height: 100%;
+
+    display: grid;
+    place-items: center;
+
+    .spinner {
+        width: 58px;
+        display: flex;
+        text-align: center;
+        margin: 100px auto 0;
+        justify-content: space-between;
+    }
+
+    .spinner > div {
+        width: 14px;
+        height: 14px;
+        background-color: var(--tertiary-foreground);
+
+        border-radius: 100%;
+        display: inline-block;
+        animation: ${skSpinner} 1.4s infinite ease-in-out both;
+    }
+
+    .spinner div:nth-child(1) {
+        animation-delay: -0.32s;
+    }
+
+    .spinner div:nth-child(2) {
+        animation-delay: -0.16s;
+    }
+
+    .ring {
+        display: inline-block;
+        position: relative;
+        width: 48px;
+        height: 52px;
+    }
+
+    .ring div {
+        width: 32px;
+        margin: 8px;
+        height: 32px;
+        display: block;
+        position: absolute;
+        border-radius: 50%;
+        box-sizing: border-box;
+        border: 2px solid #fff;
+        animation: ${prRing} 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+        border-color: #fff transparent transparent transparent;
+    }
+
+    .ring div:nth-child(1) {
+        animation-delay: -0.45s;
+    }
+
+    .ring div:nth-child(2) {
+        animation-delay: -0.3s;
+    }
+
+    .ring div:nth-child(3) {
+        animation-delay: -0.15s;
+    }
 `;
 
 interface Props {
-	type: "spinner" | "ring";
+    type: "spinner" | "ring";
 }
 
 export default function Preloader({ type }: Props) {
-	return (
-		<PreloaderBase>
-			<div class={type}>
-				<div />
-				<div />
-				<div />
-			</div>
-		</PreloaderBase>
-	);
+    return (
+        <PreloaderBase>
+            <div class={type}>
+                <div />
+                <div />
+                <div />
+            </div>
+        </PreloaderBase>
+    );
 }
diff --git a/src/components/ui/Radio.tsx b/src/components/ui/Radio.tsx
index 7bd220c..bf194cc 100644
--- a/src/components/ui/Radio.tsx
+++ b/src/components/ui/Radio.tsx
@@ -4,108 +4,108 @@ import styled, { css } from "styled-components";
 import { Children } from "../../types/Preact";
 
 interface Props {
-	children: Children;
-	description?: Children;
+    children: Children;
+    description?: Children;
 
-	checked: boolean;
-	disabled?: boolean;
-	onSelect: () => void;
+    checked: boolean;
+    disabled?: boolean;
+    onSelect: () => void;
 }
 
 interface BaseProps {
-	selected: boolean;
+    selected: boolean;
 }
 
 const RadioBase = styled.label<BaseProps>`
-	gap: 4px;
-	z-index: 1;
-	padding: 4px;
-	display: flex;
-	cursor: pointer;
-	align-items: center;
-
-	font-size: 1rem;
-	font-weight: 600;
-	user-select: none;
-	border-radius: 4px;
-	transition: 0.2s ease all;
-
-	&:hover {
-		background: var(--hover);
-	}
-
-	> input {
-		display: none;
-	}
-
-	> div {
-		margin: 4px;
-		width: 24px;
-		height: 24px;
-		display: grid;
-		border-radius: 50%;
-		place-items: center;
-		background: var(--foreground);
-
-		svg {
-			color: var(--foreground);
-			/*stroke-width: 2;*/
-		}
-	}
-
-	${(props) =>
-		props.selected &&
-		css`
-			color: white;
-			cursor: default;
-			background: var(--accent);
-
-			> div {
-				background: white;
-			}
-
-			> div svg {
-				color: var(--accent);
-			}
-
-			&:hover {
-				background: var(--accent);
-			}
-		`}
+    gap: 4px;
+    z-index: 1;
+    padding: 4px;
+    display: flex;
+    cursor: pointer;
+    align-items: center;
+
+    font-size: 1rem;
+    font-weight: 600;
+    user-select: none;
+    border-radius: 4px;
+    transition: 0.2s ease all;
+
+    &:hover {
+        background: var(--hover);
+    }
+
+    > input {
+        display: none;
+    }
+
+    > div {
+        margin: 4px;
+        width: 24px;
+        height: 24px;
+        display: grid;
+        border-radius: 50%;
+        place-items: center;
+        background: var(--foreground);
+
+        svg {
+            color: var(--foreground);
+            /*stroke-width: 2;*/
+        }
+    }
+
+    ${(props) =>
+        props.selected &&
+        css`
+            color: white;
+            cursor: default;
+            background: var(--accent);
+
+            > div {
+                background: white;
+            }
+
+            > div svg {
+                color: var(--accent);
+            }
+
+            &:hover {
+                background: var(--accent);
+            }
+        `}
 `;
 
 const RadioDescription = styled.span<BaseProps>`
-	font-size: 0.8em;
-	font-weight: 400;
-	color: var(--secondary-foreground);
-
-	${(props) =>
-		props.selected &&
-		css`
-			color: white;
-		`}
+    font-size: 0.8em;
+    font-weight: 400;
+    color: var(--secondary-foreground);
+
+    ${(props) =>
+        props.selected &&
+        css`
+            color: white;
+        `}
 `;
 
 export default function Radio(props: Props) {
-	return (
-		<RadioBase
-			selected={props.checked}
-			disabled={props.disabled}
-			onClick={() =>
-				!props.disabled && props.onSelect && props.onSelect()
-			}>
-			<div>
-				<Circle size={12} />
-			</div>
-			<input type="radio" checked={props.checked} />
-			<span>
-				<span>{props.children}</span>
-				{props.description && (
-					<RadioDescription selected={props.checked}>
-						{props.description}
-					</RadioDescription>
-				)}
-			</span>
-		</RadioBase>
-	);
+    return (
+        <RadioBase
+            selected={props.checked}
+            disabled={props.disabled}
+            onClick={() =>
+                !props.disabled && props.onSelect && props.onSelect()
+            }>
+            <div>
+                <Circle size={12} />
+            </div>
+            <input type="radio" checked={props.checked} />
+            <span>
+                <span>{props.children}</span>
+                {props.description && (
+                    <RadioDescription selected={props.checked}>
+                        {props.description}
+                    </RadioDescription>
+                )}
+            </span>
+        </RadioBase>
+    );
 }
diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx
index 3d3f296..ab087d5 100644
--- a/src/components/ui/TextArea.tsx
+++ b/src/components/ui/TextArea.tsx
@@ -1,10 +1,10 @@
 import styled, { css } from "styled-components";
 
 export interface TextAreaProps {
-	code?: boolean;
-	padding?: number;
-	lineHeight?: number;
-	hideBorder?: boolean;
+    code?: boolean;
+    padding?: number;
+    lineHeight?: number;
+    hideBorder?: boolean;
 }
 
 export const TEXT_AREA_BORDER_WIDTH = 2;
@@ -12,46 +12,46 @@ export const DEFAULT_TEXT_AREA_PADDING = 16;
 export const DEFAULT_LINE_HEIGHT = 20;
 
 export default styled.textarea<TextAreaProps>`
-	width: 100%;
-	resize: none;
-	display: block;
-	color: var(--foreground);
-	background: var(--secondary-background);
-	padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
-	line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
-
-	${(props) =>
-		props.hideBorder &&
-		css`
-			border: none;
-		`}
-
-	${(props) =>
-		!props.hideBorder &&
-		css`
-			border-radius: 4px;
-			transition: border-color 0.2s ease-in-out;
-			border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
-		`}
+    width: 100%;
+    resize: none;
+    display: block;
+    color: var(--foreground);
+    background: var(--secondary-background);
+    padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
+    line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
+
+    ${(props) =>
+        props.hideBorder &&
+        css`
+            border: none;
+        `}
+
+    ${(props) =>
+        !props.hideBorder &&
+        css`
+            border-radius: 4px;
+            transition: border-color 0.2s ease-in-out;
+            border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
+        `}
 
     &:focus {
-		outline: none;
-
-		${(props) =>
-			!props.hideBorder &&
-			css`
-				border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
-			`}
-	}
-
-	${(props) =>
-		props.code
-			? css`
-					font-family: var(--monoscape-font-font), monospace;
-			  `
-			: css`
-					font-family: inherit;
-			  `}
-
-	font-variant-ligatures: var(--ligatures);
+        outline: none;
+
+        ${(props) =>
+            !props.hideBorder &&
+            css`
+                border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
+            `}
+    }
+
+    ${(props) =>
+        props.code
+            ? css`
+                  font-family: var(--monoscape-font-font), monospace;
+              `
+            : css`
+                  font-family: inherit;
+              `}
+
+    font-variant-ligatures: var(--ligatures);
 `;
diff --git a/src/components/ui/Tip.tsx b/src/components/ui/Tip.tsx
index 0ef02c0..053af1c 100644
--- a/src/components/ui/Tip.tsx
+++ b/src/components/ui/Tip.tsx
@@ -4,66 +4,66 @@ import styled, { css } from "styled-components";
 import { Children } from "../../types/Preact";
 
 interface Props {
-	warning?: boolean;
-	error?: boolean;
+    warning?: boolean;
+    error?: boolean;
 }
 
 export const Separator = styled.div<Props>`
-	height: 1px;
-	width: calc(100% - 10px);
-	background: var(--secondary-header);
-	margin: 18px auto;
+    height: 1px;
+    width: calc(100% - 10px);
+    background: var(--secondary-header);
+    margin: 18px auto;
 `;
 
 export const TipBase = styled.div<Props>`
-	display: flex;
-	padding: 12px;
-	overflow: hidden;
-	align-items: center;
+    display: flex;
+    padding: 12px;
+    overflow: hidden;
+    align-items: center;
 
-	font-size: 14px;
-	border-radius: 7px;
-	background: var(--primary-header);
-	border: 2px solid var(--secondary-header);
+    font-size: 14px;
+    border-radius: 7px;
+    background: var(--primary-header);
+    border: 2px solid var(--secondary-header);
 
-	a {
-		cursor: pointer;
-		&:hover {
-			text-decoration: underline;
-		}
-	}
+    a {
+        cursor: pointer;
+        &:hover {
+            text-decoration: underline;
+        }
+    }
 
-	svg {
-		flex-shrink: 0;
-		margin-inline-end: 10px;
-	}
+    svg {
+        flex-shrink: 0;
+        margin-inline-end: 10px;
+    }
 
-	${(props) =>
-		props.warning &&
-		css`
-			color: var(--warning);
-			border: 2px solid var(--warning);
-			background: var(--secondary-header);
-		`}
+    ${(props) =>
+        props.warning &&
+        css`
+            color: var(--warning);
+            border: 2px solid var(--warning);
+            background: var(--secondary-header);
+        `}
 
-	${(props) =>
-		props.error &&
-		css`
-			color: var(--error);
-			border: 2px solid var(--error);
-			background: var(--secondary-header);
-		`}
+    ${(props) =>
+        props.error &&
+        css`
+            color: var(--error);
+            border: 2px solid var(--error);
+            background: var(--secondary-header);
+        `}
 `;
 
 export default function Tip(props: Props & { children: Children }) {
-	const { children, ...tipProps } = props;
-	return (
-		<>
-			<Separator />
-			<TipBase {...tipProps}>
-				<InfoCircle size={20} />
-				<span>{props.children}</span>
-			</TipBase>
-		</>
-	);
+    const { children, ...tipProps } = props;
+    return (
+        <>
+            <Separator />
+            <TipBase {...tipProps}>
+                <InfoCircle size={20} />
+                <span>{props.children}</span>
+            </TipBase>
+        </>
+    );
 }
diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx
index b9200e3..1171b42 100644
--- a/src/context/Locale.tsx
+++ b/src/context/Locale.tsx
@@ -16,202 +16,202 @@ dayjs.extend(format);
 dayjs.extend(update);
 
 export enum Language {
-	ENGLISH = "en",
-
-	ARABIC = "ar",
-	AZERBAIJANI = "az",
-	CZECH = "cs",
-	GERMAN = "de",
-	SPANISH = "es",
-	FINNISH = "fi",
-	FRENCH = "fr",
-	HINDI = "hi",
-	CROATIAN = "hr",
-	HUNGARIAN = "hu",
-	INDONESIAN = "id",
-	LITHUANIAN = "lt",
-	MACEDONIAN = "mk",
-	DUTCH = "nl",
-	POLISH = "pl",
-	PORTUGUESE_BRAZIL = "pt_BR",
-	ROMANIAN = "ro",
-	RUSSIAN = "ru",
-	SERBIAN = "sr",
-	SWEDISH = "sv",
-	TURKISH = "tr",
-	UKRANIAN = "uk",
-	CHINESE_SIMPLIFIED = "zh_Hans",
-
-	OWO = "owo",
-	PIRATE = "pr",
-	BOTTOM = "bottom",
-	PIGLATIN = "piglatin",
+    ENGLISH = "en",
+
+    ARABIC = "ar",
+    AZERBAIJANI = "az",
+    CZECH = "cs",
+    GERMAN = "de",
+    SPANISH = "es",
+    FINNISH = "fi",
+    FRENCH = "fr",
+    HINDI = "hi",
+    CROATIAN = "hr",
+    HUNGARIAN = "hu",
+    INDONESIAN = "id",
+    LITHUANIAN = "lt",
+    MACEDONIAN = "mk",
+    DUTCH = "nl",
+    POLISH = "pl",
+    PORTUGUESE_BRAZIL = "pt_BR",
+    ROMANIAN = "ro",
+    RUSSIAN = "ru",
+    SERBIAN = "sr",
+    SWEDISH = "sv",
+    TURKISH = "tr",
+    UKRANIAN = "uk",
+    CHINESE_SIMPLIFIED = "zh_Hans",
+
+    OWO = "owo",
+    PIRATE = "pr",
+    BOTTOM = "bottom",
+    PIGLATIN = "piglatin",
 }
 
 export interface LanguageEntry {
-	display: string;
-	emoji: string;
-	i18n: string;
-	dayjs?: string;
-	rtl?: boolean;
-	alt?: boolean;
+    display: string;
+    emoji: string;
+    i18n: string;
+    dayjs?: string;
+    rtl?: boolean;
+    alt?: boolean;
 }
 
 export const Languages: { [key in Language]: LanguageEntry } = {
-	en: {
-		display: "English (Traditional)",
-		emoji: "🇬🇧",
-		i18n: "en",
-		dayjs: "en-gb",
-	},
-
-	ar: { display: "عربي", emoji: "🇸🇦", i18n: "ar", rtl: true },
-	az: { display: "Azərbaycan dili", emoji: "🇦🇿", i18n: "az" },
-	cs: { display: "Čeština", emoji: "🇨🇿", i18n: "cs" },
-	de: { display: "Deutsch", emoji: "🇩🇪", i18n: "de" },
-	es: { display: "Español", emoji: "🇪🇸", i18n: "es" },
-	fi: { display: "suomi", emoji: "🇫🇮", i18n: "fi" },
-	fr: { display: "Français", emoji: "🇫🇷", i18n: "fr" },
-	hi: { display: "हिन्दी", emoji: "🇮🇳", i18n: "hi" },
-	hr: { display: "Hrvatski", emoji: "🇭🇷", i18n: "hr" },
-	hu: { display: "magyar", emoji: "🇭🇺", i18n: "hu" },
-	id: { display: "bahasa Indonesia", emoji: "🇮🇩", i18n: "id" },
-	lt: { display: "Lietuvių", emoji: "🇱🇹", i18n: "lt" },
-	mk: { display: "Македонски", emoji: "🇲🇰", i18n: "mk" },
-	nl: { display: "Nederlands", emoji: "🇳🇱", i18n: "nl" },
-	pl: { display: "Polski", emoji: "🇵🇱", i18n: "pl" },
-	pt_BR: {
-		display: "Português (do Brasil)",
-		emoji: "🇧🇷",
-		i18n: "pt_BR",
-		dayjs: "pt-br",
-	},
-	ro: { display: "Română", emoji: "🇷🇴", i18n: "ro" },
-	ru: { display: "Русский", emoji: "🇷🇺", i18n: "ru" },
-	sr: { display: "Српски", emoji: "🇷🇸", i18n: "sr" },
-	sv: { display: "Svenska", emoji: "🇸🇪", i18n: "sv" },
-	tr: { display: "Türkçe", emoji: "🇹🇷", i18n: "tr" },
-	uk: { display: "Українська", emoji: "🇺🇦", i18n: "uk" },
-	zh_Hans: {
-		display: "中文 (简体)",
-		emoji: "🇨🇳",
-		i18n: "zh_Hans",
-		dayjs: "zh",
-	},
-
-	owo: {
-		display: "OwO",
-		emoji: "🐱",
-		i18n: "owo",
-		dayjs: "en-gb",
-		alt: true,
-	},
-	pr: {
-		display: "Pirate",
-		emoji: "🏴‍☠️",
-		i18n: "pr",
-		dayjs: "en-gb",
-		alt: true,
-	},
-	bottom: {
-		display: "Bottom",
-		emoji: "🥺",
-		i18n: "bottom",
-		dayjs: "en-gb",
-		alt: true,
-	},
-	piglatin: {
-		display: "Pig Latin",
-		emoji: "🐖",
-		i18n: "piglatin",
-		dayjs: "en-gb",
-		alt: true,
-	},
+    en: {
+        display: "English (Traditional)",
+        emoji: "🇬🇧",
+        i18n: "en",
+        dayjs: "en-gb",
+    },
+
+    ar: { display: "عربي", emoji: "🇸🇦", i18n: "ar", rtl: true },
+    az: { display: "Azərbaycan dili", emoji: "🇦🇿", i18n: "az" },
+    cs: { display: "Čeština", emoji: "🇨🇿", i18n: "cs" },
+    de: { display: "Deutsch", emoji: "🇩🇪", i18n: "de" },
+    es: { display: "Español", emoji: "🇪🇸", i18n: "es" },
+    fi: { display: "suomi", emoji: "🇫🇮", i18n: "fi" },
+    fr: { display: "Français", emoji: "🇫🇷", i18n: "fr" },
+    hi: { display: "हिन्दी", emoji: "🇮🇳", i18n: "hi" },
+    hr: { display: "Hrvatski", emoji: "🇭🇷", i18n: "hr" },
+    hu: { display: "magyar", emoji: "🇭🇺", i18n: "hu" },
+    id: { display: "bahasa Indonesia", emoji: "🇮🇩", i18n: "id" },
+    lt: { display: "Lietuvių", emoji: "🇱🇹", i18n: "lt" },
+    mk: { display: "Македонски", emoji: "🇲🇰", i18n: "mk" },
+    nl: { display: "Nederlands", emoji: "🇳🇱", i18n: "nl" },
+    pl: { display: "Polski", emoji: "🇵🇱", i18n: "pl" },
+    pt_BR: {
+        display: "Português (do Brasil)",
+        emoji: "🇧🇷",
+        i18n: "pt_BR",
+        dayjs: "pt-br",
+    },
+    ro: { display: "Română", emoji: "🇷🇴", i18n: "ro" },
+    ru: { display: "Русский", emoji: "🇷🇺", i18n: "ru" },
+    sr: { display: "Српски", emoji: "🇷🇸", i18n: "sr" },
+    sv: { display: "Svenska", emoji: "🇸🇪", i18n: "sv" },
+    tr: { display: "Türkçe", emoji: "🇹🇷", i18n: "tr" },
+    uk: { display: "Українська", emoji: "🇺🇦", i18n: "uk" },
+    zh_Hans: {
+        display: "中文 (简体)",
+        emoji: "🇨🇳",
+        i18n: "zh_Hans",
+        dayjs: "zh",
+    },
+
+    owo: {
+        display: "OwO",
+        emoji: "🐱",
+        i18n: "owo",
+        dayjs: "en-gb",
+        alt: true,
+    },
+    pr: {
+        display: "Pirate",
+        emoji: "🏴‍☠️",
+        i18n: "pr",
+        dayjs: "en-gb",
+        alt: true,
+    },
+    bottom: {
+        display: "Bottom",
+        emoji: "🥺",
+        i18n: "bottom",
+        dayjs: "en-gb",
+        alt: true,
+    },
+    piglatin: {
+        display: "Pig Latin",
+        emoji: "🐖",
+        i18n: "piglatin",
+        dayjs: "en-gb",
+        alt: true,
+    },
 };
 
 interface Props {
-	children: JSX.Element | JSX.Element[];
-	locale: Language;
+    children: JSX.Element | JSX.Element[];
+    locale: Language;
 }
 
 function Locale({ children, locale }: Props) {
-	// TODO: create and use LanguageDefinition type here
-	const [defns, setDefinition] =
-		useState<Record<string, unknown>>(definition);
-	const lang = Languages[locale];
-
-	// TODO: clean this up and use the built in Intl API
-	function transformLanguage(source: { [key: string]: any }) {
-		const obj = defaultsDeep(source, definition);
-
-		const dayjs = obj.dayjs;
-		const defaults = dayjs.defaults;
-
-		const twelvehour = defaults?.twelvehour === "yes" || true;
-		const separator: "/" | "-" | "." = defaults?.date_separator ?? "/";
-		const date: "traditional" | "simplified" | "ISO8601" =
-			defaults?.date_format ?? "traditional";
-
-		const DATE_FORMATS = {
-			traditional: `DD${separator}MM${separator}YYYY`,
-			simplified: `MM${separator}DD${separator}YYYY`,
-			ISO8601: "YYYY-MM-DD",
-		};
-
-		dayjs["sameElse"] = DATE_FORMATS[date];
-		Object.keys(dayjs)
-			.filter((k) => k !== "defaults")
-			.forEach(
-				(k) =>
-					(dayjs[k] = dayjs[k].replace(
-						/{{time}}/g,
-						twelvehour ? "LT" : "HH:mm",
-					)),
-			);
-
-		return obj;
-	}
-
-	useEffect(() => {
-		if (locale === "en") {
-			const defn = transformLanguage(definition);
-			setDefinition(defn);
-			dayjs.locale("en");
-			dayjs.updateLocale("en", { calendar: defn.dayjs });
-			return;
-		}
-
-		import(`../../external/lang/${lang.i18n}.json`).then(
-			async (lang_file) => {
-				const defn = transformLanguage(lang_file.default);
-				const target = lang.dayjs ?? lang.i18n;
-				const dayjs_locale = await import(
-					`../../node_modules/dayjs/esm/locale/${target}.js`
-				);
-
-				if (defn.dayjs) {
-					dayjs.updateLocale(target, { calendar: defn.dayjs });
-				}
-
-				dayjs.locale(dayjs_locale.default);
-				setDefinition(defn);
-			},
-		);
-	}, [locale, lang]);
-
-	useEffect(() => {
-		document.body.style.direction = lang.rtl ? "rtl" : "";
-	}, [lang.rtl]);
-
-	return <IntlProvider definition={defns}>{children}</IntlProvider>;
+    // TODO: create and use LanguageDefinition type here
+    const [defns, setDefinition] =
+        useState<Record<string, unknown>>(definition);
+    const lang = Languages[locale];
+
+    // TODO: clean this up and use the built in Intl API
+    function transformLanguage(source: { [key: string]: any }) {
+        const obj = defaultsDeep(source, definition);
+
+        const dayjs = obj.dayjs;
+        const defaults = dayjs.defaults;
+
+        const twelvehour = defaults?.twelvehour === "yes" || true;
+        const separator: "/" | "-" | "." = defaults?.date_separator ?? "/";
+        const date: "traditional" | "simplified" | "ISO8601" =
+            defaults?.date_format ?? "traditional";
+
+        const DATE_FORMATS = {
+            traditional: `DD${separator}MM${separator}YYYY`,
+            simplified: `MM${separator}DD${separator}YYYY`,
+            ISO8601: "YYYY-MM-DD",
+        };
+
+        dayjs["sameElse"] = DATE_FORMATS[date];
+        Object.keys(dayjs)
+            .filter((k) => k !== "defaults")
+            .forEach(
+                (k) =>
+                    (dayjs[k] = dayjs[k].replace(
+                        /{{time}}/g,
+                        twelvehour ? "LT" : "HH:mm",
+                    )),
+            );
+
+        return obj;
+    }
+
+    useEffect(() => {
+        if (locale === "en") {
+            const defn = transformLanguage(definition);
+            setDefinition(defn);
+            dayjs.locale("en");
+            dayjs.updateLocale("en", { calendar: defn.dayjs });
+            return;
+        }
+
+        import(`../../external/lang/${lang.i18n}.json`).then(
+            async (lang_file) => {
+                const defn = transformLanguage(lang_file.default);
+                const target = lang.dayjs ?? lang.i18n;
+                const dayjs_locale = await import(
+                    `../../node_modules/dayjs/esm/locale/${target}.js`
+                );
+
+                if (defn.dayjs) {
+                    dayjs.updateLocale(target, { calendar: defn.dayjs });
+                }
+
+                dayjs.locale(dayjs_locale.default);
+                setDefinition(defn);
+            },
+        );
+    }, [locale, lang]);
+
+    useEffect(() => {
+        document.body.style.direction = lang.rtl ? "rtl" : "";
+    }, [lang.rtl]);
+
+    return <IntlProvider definition={defns}>{children}</IntlProvider>;
 }
 
 export default connectState<Omit<Props, "locale">>(
-	Locale,
-	(state) => {
-		return {
-			locale: state.locale,
-		};
-	},
-	true,
+    Locale,
+    (state) => {
+        return {
+            locale: state.locale,
+        };
+    },
+    true,
 );
diff --git a/src/context/Settings.tsx b/src/context/Settings.tsx
index 587f2a9..78e268c 100644
--- a/src/context/Settings.tsx
+++ b/src/context/Settings.tsx
@@ -13,9 +13,9 @@ import { useMemo } from "preact/hooks";
 
 import { connectState } from "../redux/connector";
 import {
-	DEFAULT_SOUNDS,
-	Settings,
-	SoundOptions,
+    DEFAULT_SOUNDS,
+    Settings,
+    SoundOptions,
 } from "../redux/reducers/settings";
 
 import { playSound, Sounds } from "../assets/sounds/Audio";
@@ -25,37 +25,37 @@ export const SettingsContext = createContext<Settings>({});
 export const SoundContext = createContext<(sound: Sounds) => void>(null!);
 
 interface Props {
-	children?: Children;
-	settings: Settings;
+    children?: Children;
+    settings: Settings;
 }
 
 function SettingsProvider({ settings, children }: Props) {
-	const play = useMemo(() => {
-		const enabled: SoundOptions = defaultsDeep(
-			settings.notification?.sounds ?? {},
-			DEFAULT_SOUNDS,
-		);
-		return (sound: Sounds) => {
-			if (enabled[sound]) {
-				playSound(sound);
-			}
-		};
-	}, [settings.notification]);
-
-	return (
-		<SettingsContext.Provider value={settings}>
-			<SoundContext.Provider value={play}>
-				{children}
-			</SoundContext.Provider>
-		</SettingsContext.Provider>
-	);
+    const play = useMemo(() => {
+        const enabled: SoundOptions = defaultsDeep(
+            settings.notification?.sounds ?? {},
+            DEFAULT_SOUNDS,
+        );
+        return (sound: Sounds) => {
+            if (enabled[sound]) {
+                playSound(sound);
+            }
+        };
+    }, [settings.notification]);
+
+    return (
+        <SettingsContext.Provider value={settings}>
+            <SoundContext.Provider value={play}>
+                {children}
+            </SoundContext.Provider>
+        </SettingsContext.Provider>
+    );
 }
 
 export default connectState<Omit<Props, "settings">>(
-	SettingsProvider,
-	(state) => {
-		return {
-			settings: state.settings,
-		};
-	},
+    SettingsProvider,
+    (state) => {
+        return {
+            settings: state.settings,
+        };
+    },
 );
diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx
index af0d218..c947d85 100644
--- a/src/context/Theme.tsx
+++ b/src/context/Theme.tsx
@@ -11,209 +11,209 @@ import { connectState } from "../redux/connector";
 import { Children } from "../types/Preact";
 
 export type Variables =
-	| "accent"
-	| "background"
-	| "foreground"
-	| "block"
-	| "message-box"
-	| "mention"
-	| "success"
-	| "warning"
-	| "error"
-	| "hover"
-	| "scrollbar-thumb"
-	| "scrollbar-track"
-	| "primary-background"
-	| "primary-header"
-	| "secondary-background"
-	| "secondary-foreground"
-	| "secondary-header"
-	| "tertiary-background"
-	| "tertiary-foreground"
-	| "status-online"
-	| "status-away"
-	| "status-busy"
-	| "status-streaming"
-	| "status-invisible";
+    | "accent"
+    | "background"
+    | "foreground"
+    | "block"
+    | "message-box"
+    | "mention"
+    | "success"
+    | "warning"
+    | "error"
+    | "hover"
+    | "scrollbar-thumb"
+    | "scrollbar-track"
+    | "primary-background"
+    | "primary-header"
+    | "secondary-background"
+    | "secondary-foreground"
+    | "secondary-header"
+    | "tertiary-background"
+    | "tertiary-foreground"
+    | "status-online"
+    | "status-away"
+    | "status-busy"
+    | "status-streaming"
+    | "status-invisible";
 
 // While this isn't used, it'd be good to keep this up to date as a reference or for future use
 export type HiddenVariables =
-	| "font"
-	| "ligatures"
-	| "app-height"
-	| "sidebar-active"
-	| "monospace-font";
+    | "font"
+    | "ligatures"
+    | "app-height"
+    | "sidebar-active"
+    | "monospace-font";
 
 export type Fonts =
-	| "Open Sans"
-	| "Inter"
-	| "Atkinson Hyperlegible"
-	| "Roboto"
-	| "Noto Sans"
-	| "Lato"
-	| "Bree Serif"
-	| "Montserrat"
-	| "Poppins"
-	| "Raleway"
-	| "Ubuntu"
-	| "Comic Neue";
+    | "Open Sans"
+    | "Inter"
+    | "Atkinson Hyperlegible"
+    | "Roboto"
+    | "Noto Sans"
+    | "Lato"
+    | "Bree Serif"
+    | "Montserrat"
+    | "Poppins"
+    | "Raleway"
+    | "Ubuntu"
+    | "Comic Neue";
 export type MonoscapeFonts =
-	| "Fira Code"
-	| "Roboto Mono"
-	| "Source Code Pro"
-	| "Space Mono"
-	| "Ubuntu Mono";
+    | "Fira Code"
+    | "Roboto Mono"
+    | "Source Code Pro"
+    | "Space Mono"
+    | "Ubuntu Mono";
 
 export type Theme = {
-	[variable in Variables]: string;
+    [variable in Variables]: string;
 } & {
-	light?: boolean;
-	font?: Fonts;
-	css?: string;
-	monoscapeFont?: MonoscapeFonts;
+    light?: boolean;
+    font?: Fonts;
+    css?: string;
+    monoscapeFont?: MonoscapeFonts;
 };
 
 export interface ThemeOptions {
-	preset?: string;
-	ligatures?: boolean;
-	custom?: Partial<Theme>;
+    preset?: string;
+    ligatures?: boolean;
+    custom?: Partial<Theme>;
 }
 
 // import aaa from "@fontsource/open-sans/300.css?raw";
 // console.info(aaa);
 
 export const FONTS: Record<Fonts, { name: string; load: () => void }> = {
-	"Open Sans": {
-		name: "Open Sans",
-		load: async () => {
-			await import("@fontsource/open-sans/300.css");
-			await import("@fontsource/open-sans/400.css");
-			await import("@fontsource/open-sans/600.css");
-			await import("@fontsource/open-sans/700.css");
-			await import("@fontsource/open-sans/400-italic.css");
-		},
-	},
-	Inter: {
-		name: "Inter",
-		load: async () => {
-			await import("@fontsource/inter/300.css");
-			await import("@fontsource/inter/400.css");
-			await import("@fontsource/inter/600.css");
-			await import("@fontsource/inter/700.css");
-		},
-	},
-	"Atkinson Hyperlegible": {
-		name: "Atkinson Hyperlegible",
-		load: async () => {
-			await import("@fontsource/atkinson-hyperlegible/400.css");
-			await import("@fontsource/atkinson-hyperlegible/700.css");
-			await import("@fontsource/atkinson-hyperlegible/400-italic.css");
-		},
-	},
-	Roboto: {
-		name: "Roboto",
-		load: async () => {
-			await import("@fontsource/roboto/400.css");
-			await import("@fontsource/roboto/700.css");
-			await import("@fontsource/roboto/400-italic.css");
-		},
-	},
-	"Noto Sans": {
-		name: "Noto Sans",
-		load: async () => {
-			await import("@fontsource/noto-sans/400.css");
-			await import("@fontsource/noto-sans/700.css");
-			await import("@fontsource/noto-sans/400-italic.css");
-		},
-	},
-	"Bree Serif": {
-		name: "Bree Serif",
-		load: () => import("@fontsource/bree-serif/400.css"),
-	},
-	Lato: {
-		name: "Lato",
-		load: async () => {
-			await import("@fontsource/lato/300.css");
-			await import("@fontsource/lato/400.css");
-			await import("@fontsource/lato/700.css");
-			await import("@fontsource/lato/400-italic.css");
-		},
-	},
-	Montserrat: {
-		name: "Montserrat",
-		load: async () => {
-			await import("@fontsource/montserrat/300.css");
-			await import("@fontsource/montserrat/400.css");
-			await import("@fontsource/montserrat/600.css");
-			await import("@fontsource/montserrat/700.css");
-			await import("@fontsource/montserrat/400-italic.css");
-		},
-	},
-	Poppins: {
-		name: "Poppins",
-		load: async () => {
-			await import("@fontsource/poppins/300.css");
-			await import("@fontsource/poppins/400.css");
-			await import("@fontsource/poppins/600.css");
-			await import("@fontsource/poppins/700.css");
-			await import("@fontsource/poppins/400-italic.css");
-		},
-	},
-	Raleway: {
-		name: "Raleway",
-		load: async () => {
-			await import("@fontsource/raleway/300.css");
-			await import("@fontsource/raleway/400.css");
-			await import("@fontsource/raleway/600.css");
-			await import("@fontsource/raleway/700.css");
-			await import("@fontsource/raleway/400-italic.css");
-		},
-	},
-	Ubuntu: {
-		name: "Ubuntu",
-		load: async () => {
-			await import("@fontsource/ubuntu/300.css");
-			await import("@fontsource/ubuntu/400.css");
-			await import("@fontsource/ubuntu/500.css");
-			await import("@fontsource/ubuntu/700.css");
-			await import("@fontsource/ubuntu/400-italic.css");
-		},
-	},
-	"Comic Neue": {
-		name: "Comic Neue",
-		load: async () => {
-			await import("@fontsource/comic-neue/300.css");
-			await import("@fontsource/comic-neue/400.css");
-			await import("@fontsource/comic-neue/700.css");
-			await import("@fontsource/comic-neue/400-italic.css");
-		},
-	},
+    "Open Sans": {
+        name: "Open Sans",
+        load: async () => {
+            await import("@fontsource/open-sans/300.css");
+            await import("@fontsource/open-sans/400.css");
+            await import("@fontsource/open-sans/600.css");
+            await import("@fontsource/open-sans/700.css");
+            await import("@fontsource/open-sans/400-italic.css");
+        },
+    },
+    Inter: {
+        name: "Inter",
+        load: async () => {
+            await import("@fontsource/inter/300.css");
+            await import("@fontsource/inter/400.css");
+            await import("@fontsource/inter/600.css");
+            await import("@fontsource/inter/700.css");
+        },
+    },
+    "Atkinson Hyperlegible": {
+        name: "Atkinson Hyperlegible",
+        load: async () => {
+            await import("@fontsource/atkinson-hyperlegible/400.css");
+            await import("@fontsource/atkinson-hyperlegible/700.css");
+            await import("@fontsource/atkinson-hyperlegible/400-italic.css");
+        },
+    },
+    Roboto: {
+        name: "Roboto",
+        load: async () => {
+            await import("@fontsource/roboto/400.css");
+            await import("@fontsource/roboto/700.css");
+            await import("@fontsource/roboto/400-italic.css");
+        },
+    },
+    "Noto Sans": {
+        name: "Noto Sans",
+        load: async () => {
+            await import("@fontsource/noto-sans/400.css");
+            await import("@fontsource/noto-sans/700.css");
+            await import("@fontsource/noto-sans/400-italic.css");
+        },
+    },
+    "Bree Serif": {
+        name: "Bree Serif",
+        load: () => import("@fontsource/bree-serif/400.css"),
+    },
+    Lato: {
+        name: "Lato",
+        load: async () => {
+            await import("@fontsource/lato/300.css");
+            await import("@fontsource/lato/400.css");
+            await import("@fontsource/lato/700.css");
+            await import("@fontsource/lato/400-italic.css");
+        },
+    },
+    Montserrat: {
+        name: "Montserrat",
+        load: async () => {
+            await import("@fontsource/montserrat/300.css");
+            await import("@fontsource/montserrat/400.css");
+            await import("@fontsource/montserrat/600.css");
+            await import("@fontsource/montserrat/700.css");
+            await import("@fontsource/montserrat/400-italic.css");
+        },
+    },
+    Poppins: {
+        name: "Poppins",
+        load: async () => {
+            await import("@fontsource/poppins/300.css");
+            await import("@fontsource/poppins/400.css");
+            await import("@fontsource/poppins/600.css");
+            await import("@fontsource/poppins/700.css");
+            await import("@fontsource/poppins/400-italic.css");
+        },
+    },
+    Raleway: {
+        name: "Raleway",
+        load: async () => {
+            await import("@fontsource/raleway/300.css");
+            await import("@fontsource/raleway/400.css");
+            await import("@fontsource/raleway/600.css");
+            await import("@fontsource/raleway/700.css");
+            await import("@fontsource/raleway/400-italic.css");
+        },
+    },
+    Ubuntu: {
+        name: "Ubuntu",
+        load: async () => {
+            await import("@fontsource/ubuntu/300.css");
+            await import("@fontsource/ubuntu/400.css");
+            await import("@fontsource/ubuntu/500.css");
+            await import("@fontsource/ubuntu/700.css");
+            await import("@fontsource/ubuntu/400-italic.css");
+        },
+    },
+    "Comic Neue": {
+        name: "Comic Neue",
+        load: async () => {
+            await import("@fontsource/comic-neue/300.css");
+            await import("@fontsource/comic-neue/400.css");
+            await import("@fontsource/comic-neue/700.css");
+            await import("@fontsource/comic-neue/400-italic.css");
+        },
+    },
 };
 
 export const MONOSCAPE_FONTS: Record<
-	MonoscapeFonts,
-	{ name: string; load: () => void }
+    MonoscapeFonts,
+    { name: string; load: () => void }
 > = {
-	"Fira Code": {
-		name: "Fira Code",
-		load: () => import("@fontsource/fira-code/400.css"),
-	},
-	"Roboto Mono": {
-		name: "Roboto Mono",
-		load: () => import("@fontsource/roboto-mono/400.css"),
-	},
-	"Source Code Pro": {
-		name: "Source Code Pro",
-		load: () => import("@fontsource/source-code-pro/400.css"),
-	},
-	"Space Mono": {
-		name: "Space Mono",
-		load: () => import("@fontsource/space-mono/400.css"),
-	},
-	"Ubuntu Mono": {
-		name: "Ubuntu Mono",
-		load: () => import("@fontsource/ubuntu-mono/400.css"),
-	},
+    "Fira Code": {
+        name: "Fira Code",
+        load: () => import("@fontsource/fira-code/400.css"),
+    },
+    "Roboto Mono": {
+        name: "Roboto Mono",
+        load: () => import("@fontsource/roboto-mono/400.css"),
+    },
+    "Source Code Pro": {
+        name: "Source Code Pro",
+        load: () => import("@fontsource/source-code-pro/400.css"),
+    },
+    "Space Mono": {
+        name: "Space Mono",
+        load: () => import("@fontsource/space-mono/400.css"),
+    },
+    "Ubuntu Mono": {
+        name: "Ubuntu Mono",
+        load: () => import("@fontsource/ubuntu-mono/400.css"),
+    },
 };
 
 export const FONT_KEYS = Object.keys(FONTS).sort();
@@ -224,70 +224,70 @@ export const DEFAULT_MONO_FONT = "Fira Code";
 
 // Generated from https://gitlab.insrt.uk/revolt/community/themes
 export const PRESETS: Record<string, Theme> = {
-	light: {
-		light: true,
-		accent: "#FD6671",
-		background: "#F6F6F6",
-		foreground: "#101010",
-		block: "#414141",
-		"message-box": "#F1F1F1",
-		mention: "rgba(251, 255, 0, 0.40)",
-		success: "#65E572",
-		warning: "#FAA352",
-		error: "#F06464",
-		hover: "rgba(0, 0, 0, 0.2)",
-		"scrollbar-thumb": "#CA525A",
-		"scrollbar-track": "transparent",
-		"primary-background": "#FFFFFF",
-		"primary-header": "#F1F1F1",
-		"secondary-background": "#F1F1F1",
-		"secondary-foreground": "#888888",
-		"secondary-header": "#F1F1F1",
-		"tertiary-background": "#4D4D4D",
-		"tertiary-foreground": "#646464",
-		"status-online": "#3ABF7E",
-		"status-away": "#F39F00",
-		"status-busy": "#F84848",
-		"status-streaming": "#977EFF",
-		"status-invisible": "#A5A5A5",
-	},
-	dark: {
-		light: false,
-		accent: "#FD6671",
-		background: "#191919",
-		foreground: "#F6F6F6",
-		block: "#2D2D2D",
-		"message-box": "#363636",
-		mention: "rgba(251, 255, 0, 0.06)",
-		success: "#65E572",
-		warning: "#FAA352",
-		error: "#F06464",
-		hover: "rgba(0, 0, 0, 0.1)",
-		"scrollbar-thumb": "#CA525A",
-		"scrollbar-track": "transparent",
-		"primary-background": "#242424",
-		"primary-header": "#363636",
-		"secondary-background": "#1E1E1E",
-		"secondary-foreground": "#C8C8C8",
-		"secondary-header": "#2D2D2D",
-		"tertiary-background": "#4D4D4D",
-		"tertiary-foreground": "#848484",
-		"status-online": "#3ABF7E",
-		"status-away": "#F39F00",
-		"status-busy": "#F84848",
-		"status-streaming": "#977EFF",
-		"status-invisible": "#A5A5A5",
-	},
+    light: {
+        light: true,
+        accent: "#FD6671",
+        background: "#F6F6F6",
+        foreground: "#101010",
+        block: "#414141",
+        "message-box": "#F1F1F1",
+        mention: "rgba(251, 255, 0, 0.40)",
+        success: "#65E572",
+        warning: "#FAA352",
+        error: "#F06464",
+        hover: "rgba(0, 0, 0, 0.2)",
+        "scrollbar-thumb": "#CA525A",
+        "scrollbar-track": "transparent",
+        "primary-background": "#FFFFFF",
+        "primary-header": "#F1F1F1",
+        "secondary-background": "#F1F1F1",
+        "secondary-foreground": "#888888",
+        "secondary-header": "#F1F1F1",
+        "tertiary-background": "#4D4D4D",
+        "tertiary-foreground": "#646464",
+        "status-online": "#3ABF7E",
+        "status-away": "#F39F00",
+        "status-busy": "#F84848",
+        "status-streaming": "#977EFF",
+        "status-invisible": "#A5A5A5",
+    },
+    dark: {
+        light: false,
+        accent: "#FD6671",
+        background: "#191919",
+        foreground: "#F6F6F6",
+        block: "#2D2D2D",
+        "message-box": "#363636",
+        mention: "rgba(251, 255, 0, 0.06)",
+        success: "#65E572",
+        warning: "#FAA352",
+        error: "#F06464",
+        hover: "rgba(0, 0, 0, 0.1)",
+        "scrollbar-thumb": "#CA525A",
+        "scrollbar-track": "transparent",
+        "primary-background": "#242424",
+        "primary-header": "#363636",
+        "secondary-background": "#1E1E1E",
+        "secondary-foreground": "#C8C8C8",
+        "secondary-header": "#2D2D2D",
+        "tertiary-background": "#4D4D4D",
+        "tertiary-foreground": "#848484",
+        "status-online": "#3ABF7E",
+        "status-away": "#F39F00",
+        "status-busy": "#F84848",
+        "status-streaming": "#977EFF",
+        "status-invisible": "#A5A5A5",
+    },
 };
 
 const keys = Object.keys(PRESETS.dark);
 const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
 :root {
 	${(props) =>
-		(Object.keys(props.theme) as Variables[]).map((key) => {
-			if (!keys.includes(key)) return;
-			return `--${key}: ${props.theme[key]};`;
-		})}
+        (Object.keys(props.theme) as Variables[]).map((key) => {
+            if (!keys.includes(key)) return;
+            return `--${key}: ${props.theme[key]};`;
+        })}
 }
 `;
 
@@ -295,66 +295,66 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
 export const ThemeContext = createContext<Theme>(PRESETS["dark"]);
 
 interface Props {
-	children: Children;
-	options?: ThemeOptions;
+    children: Children;
+    options?: ThemeOptions;
 }
 
 function Theme({ children, options }: Props) {
-	const theme: Theme = {
-		...PRESETS["dark"],
-		...PRESETS[options?.preset ?? ""],
-		...options?.custom,
-	};
+    const theme: Theme = {
+        ...PRESETS["dark"],
+        ...PRESETS[options?.preset ?? ""],
+        ...options?.custom,
+    };
 
-	const root = document.documentElement.style;
-	useEffect(() => {
-		const font = theme.font ?? DEFAULT_FONT;
-		root.setProperty("--font", `"${font}"`);
-		FONTS[font].load();
-	}, [theme.font]);
+    const root = document.documentElement.style;
+    useEffect(() => {
+        const font = theme.font ?? DEFAULT_FONT;
+        root.setProperty("--font", `"${font}"`);
+        FONTS[font].load();
+    }, [theme.font]);
 
-	useEffect(() => {
-		const font = theme.monoscapeFont ?? DEFAULT_MONO_FONT;
-		root.setProperty("--monoscape-font", `"${font}"`);
-		MONOSCAPE_FONTS[font].load();
-	}, [theme.monoscapeFont]);
+    useEffect(() => {
+        const font = theme.monoscapeFont ?? DEFAULT_MONO_FONT;
+        root.setProperty("--monoscape-font", `"${font}"`);
+        MONOSCAPE_FONTS[font].load();
+    }, [theme.monoscapeFont]);
 
-	useEffect(() => {
-		root.setProperty("--ligatures", options?.ligatures ? "normal" : "none");
-	}, [options?.ligatures]);
+    useEffect(() => {
+        root.setProperty("--ligatures", options?.ligatures ? "normal" : "none");
+    }, [options?.ligatures]);
 
-	useEffect(() => {
-		const resize = () =>
-			root.setProperty("--app-height", `${window.innerHeight}px`);
-		resize();
+    useEffect(() => {
+        const resize = () =>
+            root.setProperty("--app-height", `${window.innerHeight}px`);
+        resize();
 
-		window.addEventListener("resize", resize);
-		return () => window.removeEventListener("resize", resize);
-	}, []);
+        window.addEventListener("resize", resize);
+        return () => window.removeEventListener("resize", resize);
+    }, []);
 
-	return (
-		<ThemeContext.Provider value={theme}>
-			<Helmet>
-				<meta
-					name="theme-color"
-					content={
-						isTouchscreenDevice
-							? theme["primary-header"]
-							: theme["background"]
-					}
-				/>
-			</Helmet>
-			<GlobalTheme theme={theme} />
-			{theme.css && (
-				<style dangerouslySetInnerHTML={{ __html: theme.css }} />
-			)}
-			{children}
-		</ThemeContext.Provider>
-	);
+    return (
+        <ThemeContext.Provider value={theme}>
+            <Helmet>
+                <meta
+                    name="theme-color"
+                    content={
+                        isTouchscreenDevice
+                            ? theme["primary-header"]
+                            : theme["background"]
+                    }
+                />
+            </Helmet>
+            <GlobalTheme theme={theme} />
+            {theme.css && (
+                <style dangerouslySetInnerHTML={{ __html: theme.css }} />
+            )}
+            {children}
+        </ThemeContext.Provider>
+    );
 }
 
 export default connectState<{ children: Children }>(Theme, (state) => {
-	return {
-		options: state.settings.theme,
-	};
+    return {
+        options: state.settings.theme,
+    };
 });
diff --git a/src/context/Voice.tsx b/src/context/Voice.tsx
index b20f582..1869c0a 100644
--- a/src/context/Voice.tsx
+++ b/src/context/Voice.tsx
@@ -10,29 +10,29 @@ import { AppContext } from "./revoltjs/RevoltClient";
 import { useForceUpdate } from "./revoltjs/hooks";
 
 export enum VoiceStatus {
-	LOADING = 0,
-	UNAVAILABLE,
-	ERRORED,
-	READY = 3,
-	CONNECTING = 4,
-	AUTHENTICATING,
-	RTC_CONNECTING,
-	CONNECTED,
-	// RECONNECTING
+    LOADING = 0,
+    UNAVAILABLE,
+    ERRORED,
+    READY = 3,
+    CONNECTING = 4,
+    AUTHENTICATING,
+    RTC_CONNECTING,
+    CONNECTED,
+    // RECONNECTING
 }
 
 export interface VoiceOperations {
-	connect: (channelId: string) => Promise<void>;
-	disconnect: () => void;
-	isProducing: (type: ProduceType) => boolean;
-	startProducing: (type: ProduceType) => Promise<void>;
-	stopProducing: (type: ProduceType) => Promise<void> | undefined;
+    connect: (channelId: string) => Promise<void>;
+    disconnect: () => void;
+    isProducing: (type: ProduceType) => boolean;
+    startProducing: (type: ProduceType) => Promise<void>;
+    stopProducing: (type: ProduceType) => Promise<void> | undefined;
 }
 
 export interface VoiceState {
-	roomId?: string;
-	status: VoiceStatus;
-	participants?: Readonly<Map<string, VoiceUser>>;
+    roomId?: string;
+    status: VoiceStatus;
+    participants?: Readonly<Map<string, VoiceUser>>;
 }
 
 // They should be present from first render. - insert's words
@@ -40,168 +40,168 @@ export const VoiceContext = createContext<VoiceState>(null!);
 export const VoiceOperationsContext = createContext<VoiceOperations>(null!);
 
 type Props = {
-	children: Children;
+    children: Children;
 };
 
 export default function Voice({ children }: Props) {
-	const revoltClient = useContext(AppContext);
-	const [client, setClient] = useState<VoiceClient | undefined>(undefined);
-	const [state, setState] = useState<VoiceState>({
-		status: VoiceStatus.LOADING,
-		participants: new Map(),
-	});
-
-	function setStatus(status: VoiceStatus, roomId?: string) {
-		setState({
-			status,
-			roomId: roomId ?? client?.roomId,
-			participants: client?.participants ?? new Map(),
-		});
-	}
-
-	useEffect(() => {
-		import("../lib/vortex/VoiceClient")
-			.then(({ default: VoiceClient }) => {
-				const client = new VoiceClient();
-				setClient(client);
-
-				if (!client?.supported()) {
-					setStatus(VoiceStatus.UNAVAILABLE);
-				} else {
-					setStatus(VoiceStatus.READY);
-				}
-			})
-			.catch((err) => {
-				console.error("Failed to load voice library!", err);
-				setStatus(VoiceStatus.UNAVAILABLE);
-			});
-	}, []);
-
-	const isConnecting = useRef(false);
-	const operations: VoiceOperations = useMemo(() => {
-		return {
-			connect: async (channelId) => {
-				if (!client?.supported()) throw new Error("RTC is unavailable");
-
-				isConnecting.current = true;
-				setStatus(VoiceStatus.CONNECTING, channelId);
-
-				try {
-					const call = await revoltClient.channels.joinCall(
-						channelId,
-					);
-
-					if (!isConnecting.current) {
-						setStatus(VoiceStatus.READY);
-						return;
-					}
-
-					// ! FIXME: use configuration to check if voso is enabled
-					// await client.connect("wss://voso.revolt.chat/ws");
-					await client.connect(
-						"wss://voso.revolt.chat/ws",
-						channelId,
-					);
-
-					setStatus(VoiceStatus.AUTHENTICATING);
-
-					await client.authenticate(call.token);
-					setStatus(VoiceStatus.RTC_CONNECTING);
-
-					await client.initializeTransports();
-				} catch (error) {
-					console.error(error);
-					setStatus(VoiceStatus.READY);
-					return;
-				}
-
-				setStatus(VoiceStatus.CONNECTED);
-				isConnecting.current = false;
-			},
-			disconnect: () => {
-				if (!client?.supported()) throw new Error("RTC is unavailable");
-
-				// if (status <= VoiceStatus.READY) return;
-				// this will not update in this context
-
-				isConnecting.current = false;
-				client.disconnect();
-				setStatus(VoiceStatus.READY);
-			},
-			isProducing: (type: ProduceType) => {
-				switch (type) {
-					case "audio":
-						return client?.audioProducer !== undefined;
-				}
-			},
-			startProducing: async (type: ProduceType) => {
-				switch (type) {
-					case "audio": {
-						if (client?.audioProducer !== undefined)
-							return console.log("No audio producer."); // ! FIXME: let the user know
-						if (navigator.mediaDevices === undefined)
-							return console.log("No media devices."); // ! FIXME: let the user know
-						const mediaStream =
-							await navigator.mediaDevices.getUserMedia({
-								audio: true,
-							});
-
-						await client?.startProduce(
-							mediaStream.getAudioTracks()[0],
-							"audio",
-						);
-						return;
-					}
-				}
-			},
-			stopProducing: (type: ProduceType) => {
-				return client?.stopProduce(type);
-			},
-		};
-	}, [client]);
-
-	const { forceUpdate } = useForceUpdate();
-	const playSound = useContext(SoundContext);
-
-	useEffect(() => {
-		if (!client?.supported()) return;
-
-		// ! FIXME: message for fatal:
-		// ! get rid of these force updates
-		// ! handle it through state or smth
-
-		client.on("startProduce", forceUpdate);
-		client.on("stopProduce", forceUpdate);
-
-		client.on("userJoined", () => {
-			playSound("call_join");
-			forceUpdate();
-		});
-		client.on("userLeft", () => {
-			playSound("call_leave");
-			forceUpdate();
-		});
-		client.on("userStartProduce", forceUpdate);
-		client.on("userStopProduce", forceUpdate);
-		client.on("close", forceUpdate);
-
-		return () => {
-			client.removeListener("startProduce", forceUpdate);
-			client.removeListener("stopProduce", forceUpdate);
-
-			client.removeListener("userJoined", forceUpdate);
-			client.removeListener("userLeft", forceUpdate);
-			client.removeListener("userStartProduce", forceUpdate);
-			client.removeListener("userStopProduce", forceUpdate);
-			client.removeListener("close", forceUpdate);
-		};
-	}, [client, state]);
-
-	return (
-		<VoiceContext.Provider value={state}>
-			<VoiceOperationsContext.Provider value={operations}>
-				{children}
-			</VoiceOperationsContext.Provider>
-		</VoiceContext.Provider>
-	);
+    const revoltClient = useContext(AppContext);
+    const [client, setClient] = useState<VoiceClient | undefined>(undefined);
+    const [state, setState] = useState<VoiceState>({
+        status: VoiceStatus.LOADING,
+        participants: new Map(),
+    });
+
+    function setStatus(status: VoiceStatus, roomId?: string) {
+        setState({
+            status,
+            roomId: roomId ?? client?.roomId,
+            participants: client?.participants ?? new Map(),
+        });
+    }
+
+    useEffect(() => {
+        import("../lib/vortex/VoiceClient")
+            .then(({ default: VoiceClient }) => {
+                const client = new VoiceClient();
+                setClient(client);
+
+                if (!client?.supported()) {
+                    setStatus(VoiceStatus.UNAVAILABLE);
+                } else {
+                    setStatus(VoiceStatus.READY);
+                }
+            })
+            .catch((err) => {
+                console.error("Failed to load voice library!", err);
+                setStatus(VoiceStatus.UNAVAILABLE);
+            });
+    }, []);
+
+    const isConnecting = useRef(false);
+    const operations: VoiceOperations = useMemo(() => {
+        return {
+            connect: async (channelId) => {
+                if (!client?.supported()) throw new Error("RTC is unavailable");
+
+                isConnecting.current = true;
+                setStatus(VoiceStatus.CONNECTING, channelId);
+
+                try {
+                    const call = await revoltClient.channels.joinCall(
+                        channelId,
+                    );
+
+                    if (!isConnecting.current) {
+                        setStatus(VoiceStatus.READY);
+                        return;
+                    }
+
+                    // ! FIXME: use configuration to check if voso is enabled
+                    // await client.connect("wss://voso.revolt.chat/ws");
+                    await client.connect(
+                        "wss://voso.revolt.chat/ws",
+                        channelId,
+                    );
+
+                    setStatus(VoiceStatus.AUTHENTICATING);
+
+                    await client.authenticate(call.token);
+                    setStatus(VoiceStatus.RTC_CONNECTING);
+
+                    await client.initializeTransports();
+                } catch (error) {
+                    console.error(error);
+                    setStatus(VoiceStatus.READY);
+                    return;
+                }
+
+                setStatus(VoiceStatus.CONNECTED);
+                isConnecting.current = false;
+            },
+            disconnect: () => {
+                if (!client?.supported()) throw new Error("RTC is unavailable");
+
+                // if (status <= VoiceStatus.READY) return;
+                // this will not update in this context
+
+                isConnecting.current = false;
+                client.disconnect();
+                setStatus(VoiceStatus.READY);
+            },
+            isProducing: (type: ProduceType) => {
+                switch (type) {
+                    case "audio":
+                        return client?.audioProducer !== undefined;
+                }
+            },
+            startProducing: async (type: ProduceType) => {
+                switch (type) {
+                    case "audio": {
+                        if (client?.audioProducer !== undefined)
+                            return console.log("No audio producer."); // ! FIXME: let the user know
+                        if (navigator.mediaDevices === undefined)
+                            return console.log("No media devices."); // ! FIXME: let the user know
+                        const mediaStream =
+                            await navigator.mediaDevices.getUserMedia({
+                                audio: true,
+                            });
+
+                        await client?.startProduce(
+                            mediaStream.getAudioTracks()[0],
+                            "audio",
+                        );
+                        return;
+                    }
+                }
+            },
+            stopProducing: (type: ProduceType) => {
+                return client?.stopProduce(type);
+            },
+        };
+    }, [client]);
+
+    const { forceUpdate } = useForceUpdate();
+    const playSound = useContext(SoundContext);
+
+    useEffect(() => {
+        if (!client?.supported()) return;
+
+        // ! FIXME: message for fatal:
+        // ! get rid of these force updates
+        // ! handle it through state or smth
+
+        client.on("startProduce", forceUpdate);
+        client.on("stopProduce", forceUpdate);
+
+        client.on("userJoined", () => {
+            playSound("call_join");
+            forceUpdate();
+        });
+        client.on("userLeft", () => {
+            playSound("call_leave");
+            forceUpdate();
+        });
+        client.on("userStartProduce", forceUpdate);
+        client.on("userStopProduce", forceUpdate);
+        client.on("close", forceUpdate);
+
+        return () => {
+            client.removeListener("startProduce", forceUpdate);
+            client.removeListener("stopProduce", forceUpdate);
+
+            client.removeListener("userJoined", forceUpdate);
+            client.removeListener("userLeft", forceUpdate);
+            client.removeListener("userStartProduce", forceUpdate);
+            client.removeListener("userStopProduce", forceUpdate);
+            client.removeListener("close", forceUpdate);
+        };
+    }, [client, state]);
+
+    return (
+        <VoiceContext.Provider value={state}>
+            <VoiceOperationsContext.Provider value={operations}>
+                {children}
+            </VoiceOperationsContext.Provider>
+        </VoiceContext.Provider>
+    );
 }
diff --git a/src/context/index.tsx b/src/context/index.tsx
index f8f9472..f7a3ccb 100644
--- a/src/context/index.tsx
+++ b/src/context/index.tsx
@@ -11,21 +11,21 @@ import Intermediate from "./intermediate/Intermediate";
 import Client from "./revoltjs/RevoltClient";
 
 export default function Context({ children }: { children: Children }) {
-	return (
-		<Router>
-			<State>
-				<Theme>
-					<Settings>
-						<Locale>
-							<Intermediate>
-								<Client>
-									<Voice>{children}</Voice>
-								</Client>
-							</Intermediate>
-						</Locale>
-					</Settings>
-				</Theme>
-			</State>
-		</Router>
-	);
+    return (
+        <Router>
+            <State>
+                <Theme>
+                    <Settings>
+                        <Locale>
+                            <Intermediate>
+                                <Client>
+                                    <Voice>{children}</Voice>
+                                </Client>
+                            </Intermediate>
+                        </Locale>
+                    </Settings>
+                </Theme>
+            </State>
+        </Router>
+    );
 }
diff --git a/src/context/intermediate/Intermediate.tsx b/src/context/intermediate/Intermediate.tsx
index 8bbdfb1..22b0711 100644
--- a/src/context/intermediate/Intermediate.tsx
+++ b/src/context/intermediate/Intermediate.tsx
@@ -1,11 +1,11 @@
 import { Prompt } from "react-router";
 import { useHistory } from "react-router-dom";
 import {
-	Attachment,
-	Channels,
-	EmbedImage,
-	Servers,
-	Users,
+    Attachment,
+    Channels,
+    EmbedImage,
+    Servers,
+    Users,
 } from "revolt.js/dist/api/objects";
 
 import { createContext } from "preact";
@@ -19,161 +19,161 @@ import { Children } from "../../types/Preact";
 import Modals from "./Modals";
 
 export type Screen =
-	| { id: "none" }
-
-	// Modals
-	| { id: "signed_out" }
-	| { id: "error"; error: string }
-	| { id: "clipboard"; text: string }
-	| {
-			id: "_prompt";
-			question: Children;
-			content?: Children;
-			actions: Action[];
-	  }
-	| ({ id: "special_prompt" } & (
-			| { type: "leave_group"; target: Channels.GroupChannel }
-			| { type: "close_dm"; target: Channels.DirectMessageChannel }
-			| { type: "leave_server"; target: Servers.Server }
-			| { type: "delete_server"; target: Servers.Server }
-			| { type: "delete_channel"; target: Channels.TextChannel }
-			| { type: "delete_message"; target: Channels.Message }
-			| {
-					type: "create_invite";
-					target: Channels.TextChannel | Channels.GroupChannel;
-			  }
-			| { type: "kick_member"; target: Servers.Server; user: string }
-			| { type: "ban_member"; target: Servers.Server; user: string }
-			| { type: "unfriend_user"; target: Users.User }
-			| { type: "block_user"; target: Users.User }
-			| { type: "create_channel"; target: Servers.Server }
-	  ))
-	| ({ id: "special_input" } & (
-			| {
-					type:
-						| "create_group"
-						| "create_server"
-						| "set_custom_status"
-						| "add_friend";
-			  }
-			| {
-					type: "create_role";
-					server: string;
-					callback: (id: string) => void;
-			  }
-	  ))
-	| {
-			id: "_input";
-			question: Children;
-			field: Children;
-			defaultValue?: string;
-			callback: (value: string) => Promise<void>;
-	  }
-	| {
-			id: "onboarding";
-			callback: (
-				username: string,
-				loginAfterSuccess?: true,
-			) => Promise<void>;
-	  }
-
-	// Pop-overs
-	| { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage }
-	| { id: "modify_account"; field: "username" | "email" | "password" }
-	| { id: "profile"; user_id: string }
-	| { id: "channel_info"; channel_id: string }
-	| { id: "pending_requests"; users: string[] }
-	| {
-			id: "user_picker";
-			omit?: string[];
-			callback: (users: string[]) => Promise<void>;
-	  };
+    | { id: "none" }
+
+    // Modals
+    | { id: "signed_out" }
+    | { id: "error"; error: string }
+    | { id: "clipboard"; text: string }
+    | {
+          id: "_prompt";
+          question: Children;
+          content?: Children;
+          actions: Action[];
+      }
+    | ({ id: "special_prompt" } & (
+          | { type: "leave_group"; target: Channels.GroupChannel }
+          | { type: "close_dm"; target: Channels.DirectMessageChannel }
+          | { type: "leave_server"; target: Servers.Server }
+          | { type: "delete_server"; target: Servers.Server }
+          | { type: "delete_channel"; target: Channels.TextChannel }
+          | { type: "delete_message"; target: Channels.Message }
+          | {
+                type: "create_invite";
+                target: Channels.TextChannel | Channels.GroupChannel;
+            }
+          | { type: "kick_member"; target: Servers.Server; user: string }
+          | { type: "ban_member"; target: Servers.Server; user: string }
+          | { type: "unfriend_user"; target: Users.User }
+          | { type: "block_user"; target: Users.User }
+          | { type: "create_channel"; target: Servers.Server }
+      ))
+    | ({ id: "special_input" } & (
+          | {
+                type:
+                    | "create_group"
+                    | "create_server"
+                    | "set_custom_status"
+                    | "add_friend";
+            }
+          | {
+                type: "create_role";
+                server: string;
+                callback: (id: string) => void;
+            }
+      ))
+    | {
+          id: "_input";
+          question: Children;
+          field: Children;
+          defaultValue?: string;
+          callback: (value: string) => Promise<void>;
+      }
+    | {
+          id: "onboarding";
+          callback: (
+              username: string,
+              loginAfterSuccess?: true,
+          ) => Promise<void>;
+      }
+
+    // Pop-overs
+    | { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage }
+    | { id: "modify_account"; field: "username" | "email" | "password" }
+    | { id: "profile"; user_id: string }
+    | { id: "channel_info"; channel_id: string }
+    | { id: "pending_requests"; users: string[] }
+    | {
+          id: "user_picker";
+          omit?: string[];
+          callback: (users: string[]) => Promise<void>;
+      };
 
 export const IntermediateContext = createContext({
-	screen: { id: "none" } as Screen,
-	focusTaken: false,
+    screen: { id: "none" } as Screen,
+    focusTaken: false,
 });
 
 export const IntermediateActionsContext = createContext({
-	openScreen: (screen: Screen) => {},
-	writeClipboard: (text: string) => {},
+    openScreen: (screen: Screen) => {},
+    writeClipboard: (text: string) => {},
 });
 
 interface Props {
-	children: Children;
+    children: Children;
 }
 
 export default function Intermediate(props: Props) {
-	const [screen, openScreen] = useState<Screen>({ id: "none" });
-	const history = useHistory();
-
-	const value = {
-		screen,
-		focusTaken: screen.id !== "none",
-	};
-
-	const actions = useMemo(() => {
-		return {
-			openScreen: (screen: Screen) => openScreen(screen),
-			writeClipboard: (text: string) => {
-				if (navigator.clipboard) {
-					navigator.clipboard.writeText(text);
-				} else {
-					actions.openScreen({ id: "clipboard", text });
-				}
-			},
-		};
-	}, []);
-
-	useEffect(() => {
-		const openProfile = (user_id: string) =>
-			openScreen({ id: "profile", user_id });
-		const navigate = (path: string) => history.push(path);
-
-		const subs = [
-			internalSubscribe("Intermediate", "openProfile", openProfile),
-			internalSubscribe("Intermediate", "navigate", navigate),
-		];
-
-		return () => subs.map((unsub) => unsub());
-	}, []);
-
-	return (
-		<IntermediateContext.Provider value={value}>
-			<IntermediateActionsContext.Provider value={actions}>
-				{screen.id !== "onboarding" && props.children}
-				<Modals
-					{...value}
-					{...actions}
-					key={
-						screen.id
-					} /** By specifying a key, we reset state whenever switching screen. */
-				/>
-				<Prompt
-					when={[
-						"modify_account",
-						"special_prompt",
-						"special_input",
-						"image_viewer",
-						"profile",
-						"channel_info",
-						"pending_requests",
-						"user_picker",
-					].includes(screen.id)}
-					message={(_, action) => {
-						if (action === "POP") {
-							openScreen({ id: "none" });
-							setTimeout(() => history.push(history.location), 0);
-
-							return false;
-						}
-
-						return true;
-					}}
-				/>
-			</IntermediateActionsContext.Provider>
-		</IntermediateContext.Provider>
-	);
+    const [screen, openScreen] = useState<Screen>({ id: "none" });
+    const history = useHistory();
+
+    const value = {
+        screen,
+        focusTaken: screen.id !== "none",
+    };
+
+    const actions = useMemo(() => {
+        return {
+            openScreen: (screen: Screen) => openScreen(screen),
+            writeClipboard: (text: string) => {
+                if (navigator.clipboard) {
+                    navigator.clipboard.writeText(text);
+                } else {
+                    actions.openScreen({ id: "clipboard", text });
+                }
+            },
+        };
+    }, []);
+
+    useEffect(() => {
+        const openProfile = (user_id: string) =>
+            openScreen({ id: "profile", user_id });
+        const navigate = (path: string) => history.push(path);
+
+        const subs = [
+            internalSubscribe("Intermediate", "openProfile", openProfile),
+            internalSubscribe("Intermediate", "navigate", navigate),
+        ];
+
+        return () => subs.map((unsub) => unsub());
+    }, []);
+
+    return (
+        <IntermediateContext.Provider value={value}>
+            <IntermediateActionsContext.Provider value={actions}>
+                {screen.id !== "onboarding" && props.children}
+                <Modals
+                    {...value}
+                    {...actions}
+                    key={
+                        screen.id
+                    } /** By specifying a key, we reset state whenever switching screen. */
+                />
+                <Prompt
+                    when={[
+                        "modify_account",
+                        "special_prompt",
+                        "special_input",
+                        "image_viewer",
+                        "profile",
+                        "channel_info",
+                        "pending_requests",
+                        "user_picker",
+                    ].includes(screen.id)}
+                    message={(_, action) => {
+                        if (action === "POP") {
+                            openScreen({ id: "none" });
+                            setTimeout(() => history.push(history.location), 0);
+
+                            return false;
+                        }
+
+                        return true;
+                    }}
+                />
+            </IntermediateActionsContext.Provider>
+        </IntermediateContext.Provider>
+    );
 }
 
 export const useIntermediate = () => useContext(IntermediateActionsContext);
diff --git a/src/context/intermediate/Modals.tsx b/src/context/intermediate/Modals.tsx
index 5ca0c16..4db686d 100644
--- a/src/context/intermediate/Modals.tsx
+++ b/src/context/intermediate/Modals.tsx
@@ -7,27 +7,27 @@ import { PromptModal } from "./modals/Prompt";
 import { SignedOutModal } from "./modals/SignedOut";
 
 export interface Props {
-	screen: Screen;
-	openScreen: (id: any) => void;
+    screen: Screen;
+    openScreen: (id: any) => void;
 }
 
 export default function Modals({ screen, openScreen }: Props) {
-	const onClose = () => openScreen({ id: "none" });
+    const onClose = () => openScreen({ id: "none" });
 
-	switch (screen.id) {
-		case "_prompt":
-			return <PromptModal onClose={onClose} {...screen} />;
-		case "_input":
-			return <InputModal onClose={onClose} {...screen} />;
-		case "error":
-			return <ErrorModal onClose={onClose} {...screen} />;
-		case "signed_out":
-			return <SignedOutModal onClose={onClose} {...screen} />;
-		case "clipboard":
-			return <ClipboardModal onClose={onClose} {...screen} />;
-		case "onboarding":
-			return <OnboardingModal onClose={onClose} {...screen} />;
-	}
+    switch (screen.id) {
+        case "_prompt":
+            return <PromptModal onClose={onClose} {...screen} />;
+        case "_input":
+            return <InputModal onClose={onClose} {...screen} />;
+        case "error":
+            return <ErrorModal onClose={onClose} {...screen} />;
+        case "signed_out":
+            return <SignedOutModal onClose={onClose} {...screen} />;
+        case "clipboard":
+            return <ClipboardModal onClose={onClose} {...screen} />;
+        case "onboarding":
+            return <OnboardingModal onClose={onClose} {...screen} />;
+    }
 
-	return null;
+    return null;
 }
diff --git a/src/context/intermediate/Popovers.tsx b/src/context/intermediate/Popovers.tsx
index f171f9f..f2dc82c 100644
--- a/src/context/intermediate/Popovers.tsx
+++ b/src/context/intermediate/Popovers.tsx
@@ -11,29 +11,29 @@ import { UserPicker } from "./popovers/UserPicker";
 import { UserProfile } from "./popovers/UserProfile";
 
 export default function Popovers() {
-	const { screen } = useContext(IntermediateContext);
-	const { openScreen } = useIntermediate();
+    const { screen } = useContext(IntermediateContext);
+    const { openScreen } = useIntermediate();
 
-	const onClose = () => openScreen({ id: "none" });
+    const onClose = () => openScreen({ id: "none" });
 
-	switch (screen.id) {
-		case "profile":
-			return <UserProfile {...screen} onClose={onClose} />;
-		case "user_picker":
-			return <UserPicker {...screen} onClose={onClose} />;
-		case "image_viewer":
-			return <ImageViewer {...screen} onClose={onClose} />;
-		case "channel_info":
-			return <ChannelInfo {...screen} onClose={onClose} />;
-		case "pending_requests":
-			return <PendingRequests {...screen} onClose={onClose} />;
-		case "modify_account":
-			return <ModifyAccountModal onClose={onClose} {...screen} />;
-		case "special_prompt":
-			return <SpecialPromptModal onClose={onClose} {...screen} />;
-		case "special_input":
-			return <SpecialInputModal onClose={onClose} {...screen} />;
-	}
+    switch (screen.id) {
+        case "profile":
+            return <UserProfile {...screen} onClose={onClose} />;
+        case "user_picker":
+            return <UserPicker {...screen} onClose={onClose} />;
+        case "image_viewer":
+            return <ImageViewer {...screen} onClose={onClose} />;
+        case "channel_info":
+            return <ChannelInfo {...screen} onClose={onClose} />;
+        case "pending_requests":
+            return <PendingRequests {...screen} onClose={onClose} />;
+        case "modify_account":
+            return <ModifyAccountModal onClose={onClose} {...screen} />;
+        case "special_prompt":
+            return <SpecialPromptModal onClose={onClose} {...screen} />;
+        case "special_input":
+            return <SpecialInputModal onClose={onClose} {...screen} />;
+    }
 
-	return null;
+    return null;
 }
diff --git a/src/context/intermediate/modals/Clipboard.tsx b/src/context/intermediate/modals/Clipboard.tsx
index 8b23837..d84c43d 100644
--- a/src/context/intermediate/modals/Clipboard.tsx
+++ b/src/context/intermediate/modals/Clipboard.tsx
@@ -3,30 +3,30 @@ import { Text } from "preact-i18n";
 import Modal from "../../../components/ui/Modal";
 
 interface Props {
-	onClose: () => void;
-	text: string;
+    onClose: () => void;
+    text: string;
 }
 
 export function ClipboardModal({ onClose, text }: Props) {
-	return (
-		<Modal
-			visible={true}
-			onClose={onClose}
-			title={<Text id="app.special.modals.clipboard.unavailable" />}
-			actions={[
-				{
-					onClick: onClose,
-					confirmation: true,
-					text: <Text id="app.special.modals.actions.close" />,
-				},
-			]}>
-			{location.protocol !== "https:" && (
-				<p>
-					<Text id="app.special.modals.clipboard.https" />
-				</p>
-			)}
-			<Text id="app.special.modals.clipboard.copy" />{" "}
-			<code style={{ userSelect: "all" }}>{text}</code>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            onClose={onClose}
+            title={<Text id="app.special.modals.clipboard.unavailable" />}
+            actions={[
+                {
+                    onClick: onClose,
+                    confirmation: true,
+                    text: <Text id="app.special.modals.actions.close" />,
+                },
+            ]}>
+            {location.protocol !== "https:" && (
+                <p>
+                    <Text id="app.special.modals.clipboard.https" />
+                </p>
+            )}
+            <Text id="app.special.modals.clipboard.copy" />{" "}
+            <code style={{ userSelect: "all" }}>{text}</code>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/modals/Error.tsx b/src/context/intermediate/modals/Error.tsx
index 81d1152..64cadeb 100644
--- a/src/context/intermediate/modals/Error.tsx
+++ b/src/context/intermediate/modals/Error.tsx
@@ -3,28 +3,28 @@ import { Text } from "preact-i18n";
 import Modal from "../../../components/ui/Modal";
 
 interface Props {
-	onClose: () => void;
-	error: string;
+    onClose: () => void;
+    error: string;
 }
 
 export function ErrorModal({ onClose, error }: Props) {
-	return (
-		<Modal
-			visible={true}
-			onClose={() => false}
-			title={<Text id="app.special.modals.error" />}
-			actions={[
-				{
-					onClick: onClose,
-					confirmation: true,
-					text: <Text id="app.special.modals.actions.ok" />,
-				},
-				{
-					onClick: () => location.reload(),
-					text: <Text id="app.special.modals.actions.reload" />,
-				},
-			]}>
-			<Text id={`error.${error}`}>{error}</Text>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            onClose={() => false}
+            title={<Text id="app.special.modals.error" />}
+            actions={[
+                {
+                    onClick: onClose,
+                    confirmation: true,
+                    text: <Text id="app.special.modals.actions.ok" />,
+                },
+                {
+                    onClick: () => location.reload(),
+                    text: <Text id="app.special.modals.actions.reload" />,
+                },
+            ]}>
+            <Text id={`error.${error}`}>{error}</Text>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/modals/Input.tsx b/src/context/intermediate/modals/Input.tsx
index 36e6026..9f2b37a 100644
--- a/src/context/intermediate/modals/Input.tsx
+++ b/src/context/intermediate/modals/Input.tsx
@@ -13,164 +13,164 @@ import { AppContext } from "../../revoltjs/RevoltClient";
 import { takeError } from "../../revoltjs/util";
 
 interface Props {
-	onClose: () => void;
-	question: Children;
-	field?: Children;
-	defaultValue?: string;
-	callback: (value: string) => Promise<void>;
+    onClose: () => void;
+    question: Children;
+    field?: Children;
+    defaultValue?: string;
+    callback: (value: string) => Promise<void>;
 }
 
 export function InputModal({
-	onClose,
-	question,
-	field,
-	defaultValue,
-	callback,
+    onClose,
+    question,
+    field,
+    defaultValue,
+    callback,
 }: Props) {
-	const [processing, setProcessing] = useState(false);
-	const [value, setValue] = useState(defaultValue ?? "");
-	const [error, setError] = useState<undefined | string>(undefined);
+    const [processing, setProcessing] = useState(false);
+    const [value, setValue] = useState(defaultValue ?? "");
+    const [error, setError] = useState<undefined | string>(undefined);
 
-	return (
-		<Modal
-			visible={true}
-			title={question}
-			disabled={processing}
-			actions={[
-				{
-					confirmation: true,
-					text: <Text id="app.special.modals.actions.ok" />,
-					onClick: () => {
-						setProcessing(true);
-						callback(value)
-							.then(onClose)
-							.catch((err) => {
-								setError(takeError(err));
-								setProcessing(false);
-							});
-					},
-				},
-				{
-					text: <Text id="app.special.modals.actions.cancel" />,
-					onClick: onClose,
-				},
-			]}
-			onClose={onClose}>
-			<form>
-				{field ? (
-					<Overline error={error} block>
-						{field}
-					</Overline>
-				) : (
-					error && <Overline error={error} type="error" block />
-				)}
-				<InputBox
-					value={value}
-					onChange={(e) => setValue(e.currentTarget.value)}
-				/>
-			</form>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            title={question}
+            disabled={processing}
+            actions={[
+                {
+                    confirmation: true,
+                    text: <Text id="app.special.modals.actions.ok" />,
+                    onClick: () => {
+                        setProcessing(true);
+                        callback(value)
+                            .then(onClose)
+                            .catch((err) => {
+                                setError(takeError(err));
+                                setProcessing(false);
+                            });
+                    },
+                },
+                {
+                    text: <Text id="app.special.modals.actions.cancel" />,
+                    onClick: onClose,
+                },
+            ]}
+            onClose={onClose}>
+            <form>
+                {field ? (
+                    <Overline error={error} block>
+                        {field}
+                    </Overline>
+                ) : (
+                    error && <Overline error={error} type="error" block />
+                )}
+                <InputBox
+                    value={value}
+                    onChange={(e) => setValue(e.currentTarget.value)}
+                />
+            </form>
+        </Modal>
+    );
 }
 
 type SpecialProps = { onClose: () => void } & (
-	| {
-			type:
-				| "create_group"
-				| "create_server"
-				| "set_custom_status"
-				| "add_friend";
-	  }
-	| { type: "create_role"; server: string; callback: (id: string) => void }
+    | {
+          type:
+              | "create_group"
+              | "create_server"
+              | "set_custom_status"
+              | "add_friend";
+      }
+    | { type: "create_role"; server: string; callback: (id: string) => void }
 );
 
 export function SpecialInputModal(props: SpecialProps) {
-	const history = useHistory();
-	const client = useContext(AppContext);
+    const history = useHistory();
+    const client = useContext(AppContext);
 
-	const { onClose } = props;
-	switch (props.type) {
-		case "create_group": {
-			return (
-				<InputModal
-					onClose={onClose}
-					question={<Text id="app.main.groups.create" />}
-					field={<Text id="app.main.groups.name" />}
-					callback={async (name) => {
-						const group = await client.channels.createGroup({
-							name,
-							nonce: ulid(),
-							users: [],
-						});
+    const { onClose } = props;
+    switch (props.type) {
+        case "create_group": {
+            return (
+                <InputModal
+                    onClose={onClose}
+                    question={<Text id="app.main.groups.create" />}
+                    field={<Text id="app.main.groups.name" />}
+                    callback={async (name) => {
+                        const group = await client.channels.createGroup({
+                            name,
+                            nonce: ulid(),
+                            users: [],
+                        });
 
-						history.push(`/channel/${group._id}`);
-					}}
-				/>
-			);
-		}
-		case "create_server": {
-			return (
-				<InputModal
-					onClose={onClose}
-					question={<Text id="app.main.servers.create" />}
-					field={<Text id="app.main.servers.name" />}
-					callback={async (name) => {
-						const server = await client.servers.createServer({
-							name,
-							nonce: ulid(),
-						});
+                        history.push(`/channel/${group._id}`);
+                    }}
+                />
+            );
+        }
+        case "create_server": {
+            return (
+                <InputModal
+                    onClose={onClose}
+                    question={<Text id="app.main.servers.create" />}
+                    field={<Text id="app.main.servers.name" />}
+                    callback={async (name) => {
+                        const server = await client.servers.createServer({
+                            name,
+                            nonce: ulid(),
+                        });
 
-						history.push(`/server/${server._id}`);
-					}}
-				/>
-			);
-		}
-		case "create_role": {
-			return (
-				<InputModal
-					onClose={onClose}
-					question={
-						<Text id="app.settings.permissions.create_role" />
-					}
-					field={<Text id="app.settings.permissions.role_name" />}
-					callback={async (name) => {
-						const role = await client.servers.createRole(
-							props.server,
-							name,
-						);
-						props.callback(role.id);
-					}}
-				/>
-			);
-		}
-		case "set_custom_status": {
-			return (
-				<InputModal
-					onClose={onClose}
-					question={<Text id="app.context_menu.set_custom_status" />}
-					field={<Text id="app.context_menu.custom_status" />}
-					defaultValue={client.user?.status?.text}
-					callback={(text) =>
-						client.users.editUser({
-							status: {
-								...client.user?.status,
-								text: text.trim().length > 0 ? text : undefined,
-							},
-						})
-					}
-				/>
-			);
-		}
-		case "add_friend": {
-			return (
-				<InputModal
-					onClose={onClose}
-					question={"Add Friend"}
-					callback={(username) => client.users.addFriend(username)}
-				/>
-			);
-		}
-		default:
-			return null;
-	}
+                        history.push(`/server/${server._id}`);
+                    }}
+                />
+            );
+        }
+        case "create_role": {
+            return (
+                <InputModal
+                    onClose={onClose}
+                    question={
+                        <Text id="app.settings.permissions.create_role" />
+                    }
+                    field={<Text id="app.settings.permissions.role_name" />}
+                    callback={async (name) => {
+                        const role = await client.servers.createRole(
+                            props.server,
+                            name,
+                        );
+                        props.callback(role.id);
+                    }}
+                />
+            );
+        }
+        case "set_custom_status": {
+            return (
+                <InputModal
+                    onClose={onClose}
+                    question={<Text id="app.context_menu.set_custom_status" />}
+                    field={<Text id="app.context_menu.custom_status" />}
+                    defaultValue={client.user?.status?.text}
+                    callback={(text) =>
+                        client.users.editUser({
+                            status: {
+                                ...client.user?.status,
+                                text: text.trim().length > 0 ? text : undefined,
+                            },
+                        })
+                    }
+                />
+            );
+        }
+        case "add_friend": {
+            return (
+                <InputModal
+                    onClose={onClose}
+                    question={"Add Friend"}
+                    callback={(username) => client.users.addFriend(username)}
+                />
+            );
+        }
+        default:
+            return null;
+    }
 }
diff --git a/src/context/intermediate/modals/Onboarding.tsx b/src/context/intermediate/modals/Onboarding.tsx
index 35934bb..b5db430 100644
--- a/src/context/intermediate/modals/Onboarding.tsx
+++ b/src/context/intermediate/modals/Onboarding.tsx
@@ -12,67 +12,67 @@ import FormField from "../../../pages/login/FormField";
 import { takeError } from "../../revoltjs/util";
 
 interface Props {
-	onClose: () => void;
-	callback: (username: string, loginAfterSuccess?: true) => Promise<void>;
+    onClose: () => void;
+    callback: (username: string, loginAfterSuccess?: true) => Promise<void>;
 }
 
 interface FormInputs {
-	username: string;
+    username: string;
 }
 
 export function OnboardingModal({ onClose, callback }: Props) {
-	const { handleSubmit, register } = useForm<FormInputs>();
-	const [loading, setLoading] = useState(false);
-	const [error, setError] = useState<string | undefined>(undefined);
+    const { handleSubmit, register } = useForm<FormInputs>();
+    const [loading, setLoading] = useState(false);
+    const [error, setError] = useState<string | undefined>(undefined);
 
-	const onSubmit: SubmitHandler<FormInputs> = ({ username }) => {
-		setLoading(true);
-		callback(username, true)
-			.then(onClose)
-			.catch((err: any) => {
-				setError(takeError(err));
-				setLoading(false);
-			});
-	};
+    const onSubmit: SubmitHandler<FormInputs> = ({ username }) => {
+        setLoading(true);
+        callback(username, true)
+            .then(onClose)
+            .catch((err: any) => {
+                setError(takeError(err));
+                setLoading(false);
+            });
+    };
 
-	return (
-		<div className={styles.onboarding}>
-			<div className={styles.header}>
-				<h1>
-					<Text id="app.special.modals.onboarding.welcome" />
-					<img src={wideSVG} />
-				</h1>
-			</div>
-			<div className={styles.form}>
-				{loading ? (
-					<Preloader type="spinner" />
-				) : (
-					<>
-						<p>
-							<Text id="app.special.modals.onboarding.pick" />
-						</p>
-						<form
-							onSubmit={
-								handleSubmit(
-									onSubmit,
-								) as JSX.GenericEventHandler<HTMLFormElement>
-							}>
-							<div>
-								<FormField
-									type="username"
-									register={register}
-									showOverline
-									error={error}
-								/>
-							</div>
-							<Button type="submit">
-								<Text id="app.special.modals.actions.continue" />
-							</Button>
-						</form>
-					</>
-				)}
-			</div>
-			<div />
-		</div>
-	);
+    return (
+        <div className={styles.onboarding}>
+            <div className={styles.header}>
+                <h1>
+                    <Text id="app.special.modals.onboarding.welcome" />
+                    <img src={wideSVG} />
+                </h1>
+            </div>
+            <div className={styles.form}>
+                {loading ? (
+                    <Preloader type="spinner" />
+                ) : (
+                    <>
+                        <p>
+                            <Text id="app.special.modals.onboarding.pick" />
+                        </p>
+                        <form
+                            onSubmit={
+                                handleSubmit(
+                                    onSubmit,
+                                ) as JSX.GenericEventHandler<HTMLFormElement>
+                            }>
+                            <div>
+                                <FormField
+                                    type="username"
+                                    register={register}
+                                    showOverline
+                                    error={error}
+                                />
+                            </div>
+                            <Button type="submit">
+                                <Text id="app.special.modals.actions.continue" />
+                            </Button>
+                        </form>
+                    </>
+                )}
+            </div>
+            <div />
+        </div>
+    );
 }
diff --git a/src/context/intermediate/modals/Prompt.tsx b/src/context/intermediate/modals/Prompt.tsx
index dd21a9c..c09e1bb 100644
--- a/src/context/intermediate/modals/Prompt.tsx
+++ b/src/context/intermediate/modals/Prompt.tsx
@@ -21,453 +21,453 @@ import { mapMessage, takeError } from "../../revoltjs/util";
 import { useIntermediate } from "../Intermediate";
 
 interface Props {
-	onClose: () => void;
-	question: Children;
-	content?: Children;
-	disabled?: boolean;
-	actions: Action[];
-	error?: string;
+    onClose: () => void;
+    question: Children;
+    content?: Children;
+    disabled?: boolean;
+    actions: Action[];
+    error?: string;
 }
 
 export function PromptModal({
-	onClose,
-	question,
-	content,
-	actions,
-	disabled,
-	error,
+    onClose,
+    question,
+    content,
+    actions,
+    disabled,
+    error,
 }: Props) {
-	return (
-		<Modal
-			visible={true}
-			title={question}
-			actions={actions}
-			onClose={onClose}
-			disabled={disabled}>
-			{error && <Overline error={error} type="error" />}
-			{content}
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            title={question}
+            actions={actions}
+            onClose={onClose}
+            disabled={disabled}>
+            {error && <Overline error={error} type="error" />}
+            {content}
+        </Modal>
+    );
 }
 
 type SpecialProps = { onClose: () => void } & (
-	| { type: "leave_group"; target: Channels.GroupChannel }
-	| { type: "close_dm"; target: Channels.DirectMessageChannel }
-	| { type: "leave_server"; target: Servers.Server }
-	| { type: "delete_server"; target: Servers.Server }
-	| { type: "delete_channel"; target: Channels.TextChannel }
-	| { type: "delete_message"; target: Channels.Message }
-	| {
-			type: "create_invite";
-			target: Channels.TextChannel | Channels.GroupChannel;
-	  }
-	| { type: "kick_member"; target: Servers.Server; user: string }
-	| { type: "ban_member"; target: Servers.Server; user: string }
-	| { type: "unfriend_user"; target: Users.User }
-	| { type: "block_user"; target: Users.User }
-	| { type: "create_channel"; target: Servers.Server }
+    | { type: "leave_group"; target: Channels.GroupChannel }
+    | { type: "close_dm"; target: Channels.DirectMessageChannel }
+    | { type: "leave_server"; target: Servers.Server }
+    | { type: "delete_server"; target: Servers.Server }
+    | { type: "delete_channel"; target: Channels.TextChannel }
+    | { type: "delete_message"; target: Channels.Message }
+    | {
+          type: "create_invite";
+          target: Channels.TextChannel | Channels.GroupChannel;
+      }
+    | { type: "kick_member"; target: Servers.Server; user: string }
+    | { type: "ban_member"; target: Servers.Server; user: string }
+    | { type: "unfriend_user"; target: Users.User }
+    | { type: "block_user"; target: Users.User }
+    | { type: "create_channel"; target: Servers.Server }
 );
 
 export function SpecialPromptModal(props: SpecialProps) {
-	const client = useContext(AppContext);
-	const [processing, setProcessing] = useState(false);
-	const [error, setError] = useState<undefined | string>(undefined);
+    const client = useContext(AppContext);
+    const [processing, setProcessing] = useState(false);
+    const [error, setError] = useState<undefined | string>(undefined);
 
-	const { onClose } = props;
-	switch (props.type) {
-		case "leave_group":
-		case "close_dm":
-		case "leave_server":
-		case "delete_server":
-		case "delete_channel":
-		case "unfriend_user":
-		case "block_user": {
-			const EVENTS = {
-				close_dm: ["confirm_close_dm", "close"],
-				delete_server: ["confirm_delete", "delete"],
-				delete_channel: ["confirm_delete", "delete"],
-				leave_group: ["confirm_leave", "leave"],
-				leave_server: ["confirm_leave", "leave"],
-				unfriend_user: ["unfriend_user", "remove"],
-				block_user: ["block_user", "block"],
-			};
+    const { onClose } = props;
+    switch (props.type) {
+        case "leave_group":
+        case "close_dm":
+        case "leave_server":
+        case "delete_server":
+        case "delete_channel":
+        case "unfriend_user":
+        case "block_user": {
+            const EVENTS = {
+                close_dm: ["confirm_close_dm", "close"],
+                delete_server: ["confirm_delete", "delete"],
+                delete_channel: ["confirm_delete", "delete"],
+                leave_group: ["confirm_leave", "leave"],
+                leave_server: ["confirm_leave", "leave"],
+                unfriend_user: ["unfriend_user", "remove"],
+                block_user: ["block_user", "block"],
+            };
 
-			let event = EVENTS[props.type];
-			let name;
-			switch (props.type) {
-				case "unfriend_user":
-				case "block_user":
-					name = props.target.username;
-					break;
-				case "close_dm":
-					name = client.users.get(
-						client.channels.getRecipient(props.target._id),
-					)?.username;
-					break;
-				default:
-					name = props.target.name;
-			}
+            let event = EVENTS[props.type];
+            let name;
+            switch (props.type) {
+                case "unfriend_user":
+                case "block_user":
+                    name = props.target.username;
+                    break;
+                case "close_dm":
+                    name = client.users.get(
+                        client.channels.getRecipient(props.target._id),
+                    )?.username;
+                    break;
+                default:
+                    name = props.target.name;
+            }
 
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={
-						<Text
-							id={`app.special.modals.prompt.${event[0]}`}
-							fields={{ name }}
-						/>
-					}
-					actions={[
-						{
-							confirmation: true,
-							contrast: true,
-							error: true,
-							text: (
-								<Text
-									id={`app.special.modals.actions.${event[1]}`}
-								/>
-							),
-							onClick: async () => {
-								setProcessing(true);
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={
+                        <Text
+                            id={`app.special.modals.prompt.${event[0]}`}
+                            fields={{ name }}
+                        />
+                    }
+                    actions={[
+                        {
+                            confirmation: true,
+                            contrast: true,
+                            error: true,
+                            text: (
+                                <Text
+                                    id={`app.special.modals.actions.${event[1]}`}
+                                />
+                            ),
+                            onClick: async () => {
+                                setProcessing(true);
 
-								try {
-									switch (props.type) {
-										case "unfriend_user":
-											await client.users.removeFriend(
-												props.target._id,
-											);
-											break;
-										case "block_user":
-											await client.users.blockUser(
-												props.target._id,
-											);
-											break;
-										case "leave_group":
-										case "close_dm":
-										case "delete_channel":
-											await client.channels.delete(
-												props.target._id,
-											);
-											break;
-										case "leave_server":
-										case "delete_server":
-											await client.servers.delete(
-												props.target._id,
-											);
-											break;
-									}
+                                try {
+                                    switch (props.type) {
+                                        case "unfriend_user":
+                                            await client.users.removeFriend(
+                                                props.target._id,
+                                            );
+                                            break;
+                                        case "block_user":
+                                            await client.users.blockUser(
+                                                props.target._id,
+                                            );
+                                            break;
+                                        case "leave_group":
+                                        case "close_dm":
+                                        case "delete_channel":
+                                            await client.channels.delete(
+                                                props.target._id,
+                                            );
+                                            break;
+                                        case "leave_server":
+                                        case "delete_server":
+                                            await client.servers.delete(
+                                                props.target._id,
+                                            );
+                                            break;
+                                    }
 
-									onClose();
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							},
-						},
-						{
-							text: (
-								<Text id="app.special.modals.actions.cancel" />
-							),
-							onClick: onClose,
-						},
-					]}
-					content={
-						<TextReact
-							id={`app.special.modals.prompt.${event[0]}_long`}
-							fields={{ name: <b>{name}</b> }}
-						/>
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		case "delete_message": {
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={<Text id={"app.context_menu.delete_message"} />}
-					actions={[
-						{
-							confirmation: true,
-							contrast: true,
-							error: true,
-							text: (
-								<Text id="app.special.modals.actions.delete" />
-							),
-							onClick: async () => {
-								setProcessing(true);
+                                    onClose();
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            },
+                        },
+                        {
+                            text: (
+                                <Text id="app.special.modals.actions.cancel" />
+                            ),
+                            onClick: onClose,
+                        },
+                    ]}
+                    content={
+                        <TextReact
+                            id={`app.special.modals.prompt.${event[0]}_long`}
+                            fields={{ name: <b>{name}</b> }}
+                        />
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        case "delete_message": {
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={<Text id={"app.context_menu.delete_message"} />}
+                    actions={[
+                        {
+                            confirmation: true,
+                            contrast: true,
+                            error: true,
+                            text: (
+                                <Text id="app.special.modals.actions.delete" />
+                            ),
+                            onClick: async () => {
+                                setProcessing(true);
 
-								try {
-									await client.channels.deleteMessage(
-										props.target.channel,
-										props.target._id,
-									);
+                                try {
+                                    await client.channels.deleteMessage(
+                                        props.target.channel,
+                                        props.target._id,
+                                    );
 
-									onClose();
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							},
-						},
-						{
-							text: (
-								<Text id="app.special.modals.actions.cancel" />
-							),
-							onClick: onClose,
-						},
-					]}
-					content={
-						<>
-							<Text
-								id={`app.special.modals.prompt.confirm_delete_message_long`}
-							/>
-							<Message
-								message={mapMessage(props.target)}
-								head={true}
-								contrast
-							/>
-						</>
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		case "create_invite": {
-			const [code, setCode] = useState("abcdef");
-			const { writeClipboard } = useIntermediate();
+                                    onClose();
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            },
+                        },
+                        {
+                            text: (
+                                <Text id="app.special.modals.actions.cancel" />
+                            ),
+                            onClick: onClose,
+                        },
+                    ]}
+                    content={
+                        <>
+                            <Text
+                                id={`app.special.modals.prompt.confirm_delete_message_long`}
+                            />
+                            <Message
+                                message={mapMessage(props.target)}
+                                head={true}
+                                contrast
+                            />
+                        </>
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        case "create_invite": {
+            const [code, setCode] = useState("abcdef");
+            const { writeClipboard } = useIntermediate();
 
-			useEffect(() => {
-				setProcessing(true);
+            useEffect(() => {
+                setProcessing(true);
 
-				client.channels
-					.createInvite(props.target._id)
-					.then((code) => setCode(code))
-					.catch((err) => setError(takeError(err)))
-					.finally(() => setProcessing(false));
-			}, []);
+                client.channels
+                    .createInvite(props.target._id)
+                    .then((code) => setCode(code))
+                    .catch((err) => setError(takeError(err)))
+                    .finally(() => setProcessing(false));
+            }, []);
 
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={<Text id={`app.context_menu.create_invite`} />}
-					actions={[
-						{
-							text: <Text id="app.special.modals.actions.ok" />,
-							confirmation: true,
-							onClick: onClose,
-						},
-						{
-							text: <Text id="app.context_menu.copy_link" />,
-							onClick: () =>
-								writeClipboard(
-									`${window.location.protocol}//${window.location.host}/invite/${code}`,
-								),
-						},
-					]}
-					content={
-						processing ? (
-							<Text id="app.special.modals.prompt.create_invite_generate" />
-						) : (
-							<div className={styles.invite}>
-								<Text id="app.special.modals.prompt.create_invite_created" />
-								<code>{code}</code>
-							</div>
-						)
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		case "kick_member": {
-			const user = client.users.get(props.user);
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={<Text id={`app.context_menu.create_invite`} />}
+                    actions={[
+                        {
+                            text: <Text id="app.special.modals.actions.ok" />,
+                            confirmation: true,
+                            onClick: onClose,
+                        },
+                        {
+                            text: <Text id="app.context_menu.copy_link" />,
+                            onClick: () =>
+                                writeClipboard(
+                                    `${window.location.protocol}//${window.location.host}/invite/${code}`,
+                                ),
+                        },
+                    ]}
+                    content={
+                        processing ? (
+                            <Text id="app.special.modals.prompt.create_invite_generate" />
+                        ) : (
+                            <div className={styles.invite}>
+                                <Text id="app.special.modals.prompt.create_invite_created" />
+                                <code>{code}</code>
+                            </div>
+                        )
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        case "kick_member": {
+            const user = client.users.get(props.user);
 
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={<Text id={`app.context_menu.kick_member`} />}
-					actions={[
-						{
-							text: <Text id="app.special.modals.actions.kick" />,
-							contrast: true,
-							error: true,
-							confirmation: true,
-							onClick: async () => {
-								setProcessing(true);
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={<Text id={`app.context_menu.kick_member`} />}
+                    actions={[
+                        {
+                            text: <Text id="app.special.modals.actions.kick" />,
+                            contrast: true,
+                            error: true,
+                            confirmation: true,
+                            onClick: async () => {
+                                setProcessing(true);
 
-								try {
-									await client.servers.members.kickMember(
-										props.target._id,
-										props.user,
-									);
-									onClose();
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							},
-						},
-						{
-							text: (
-								<Text id="app.special.modals.actions.cancel" />
-							),
-							onClick: onClose,
-						},
-					]}
-					content={
-						<div className={styles.column}>
-							<UserIcon target={user} size={64} />
-							<Text
-								id="app.special.modals.prompt.confirm_kick"
-								fields={{ name: user?.username }}
-							/>
-						</div>
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		case "ban_member": {
-			const [reason, setReason] = useState<string | undefined>(undefined);
-			const user = client.users.get(props.user);
+                                try {
+                                    await client.servers.members.kickMember(
+                                        props.target._id,
+                                        props.user,
+                                    );
+                                    onClose();
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            },
+                        },
+                        {
+                            text: (
+                                <Text id="app.special.modals.actions.cancel" />
+                            ),
+                            onClick: onClose,
+                        },
+                    ]}
+                    content={
+                        <div className={styles.column}>
+                            <UserIcon target={user} size={64} />
+                            <Text
+                                id="app.special.modals.prompt.confirm_kick"
+                                fields={{ name: user?.username }}
+                            />
+                        </div>
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        case "ban_member": {
+            const [reason, setReason] = useState<string | undefined>(undefined);
+            const user = client.users.get(props.user);
 
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={<Text id={`app.context_menu.ban_member`} />}
-					actions={[
-						{
-							text: <Text id="app.special.modals.actions.ban" />,
-							contrast: true,
-							error: true,
-							confirmation: true,
-							onClick: async () => {
-								setProcessing(true);
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={<Text id={`app.context_menu.ban_member`} />}
+                    actions={[
+                        {
+                            text: <Text id="app.special.modals.actions.ban" />,
+                            contrast: true,
+                            error: true,
+                            confirmation: true,
+                            onClick: async () => {
+                                setProcessing(true);
 
-								try {
-									await client.servers.banUser(
-										props.target._id,
-										props.user,
-										{ reason },
-									);
-									onClose();
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							},
-						},
-						{
-							text: (
-								<Text id="app.special.modals.actions.cancel" />
-							),
-							onClick: onClose,
-						},
-					]}
-					content={
-						<div className={styles.column}>
-							<UserIcon target={user} size={64} />
-							<Text
-								id="app.special.modals.prompt.confirm_ban"
-								fields={{ name: user?.username }}
-							/>
-							<Overline>
-								<Text id="app.special.modals.prompt.confirm_ban_reason" />
-							</Overline>
-							<InputBox
-								value={reason ?? ""}
-								onChange={(e) =>
-									setReason(e.currentTarget.value)
-								}
-							/>
-						</div>
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		case "create_channel": {
-			const [name, setName] = useState("");
-			const [type, setType] = useState<"Text" | "Voice">("Text");
-			const history = useHistory();
+                                try {
+                                    await client.servers.banUser(
+                                        props.target._id,
+                                        props.user,
+                                        { reason },
+                                    );
+                                    onClose();
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            },
+                        },
+                        {
+                            text: (
+                                <Text id="app.special.modals.actions.cancel" />
+                            ),
+                            onClick: onClose,
+                        },
+                    ]}
+                    content={
+                        <div className={styles.column}>
+                            <UserIcon target={user} size={64} />
+                            <Text
+                                id="app.special.modals.prompt.confirm_ban"
+                                fields={{ name: user?.username }}
+                            />
+                            <Overline>
+                                <Text id="app.special.modals.prompt.confirm_ban_reason" />
+                            </Overline>
+                            <InputBox
+                                value={reason ?? ""}
+                                onChange={(e) =>
+                                    setReason(e.currentTarget.value)
+                                }
+                            />
+                        </div>
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        case "create_channel": {
+            const [name, setName] = useState("");
+            const [type, setType] = useState<"Text" | "Voice">("Text");
+            const history = useHistory();
 
-			return (
-				<PromptModal
-					onClose={onClose}
-					question={<Text id="app.context_menu.create_channel" />}
-					actions={[
-						{
-							confirmation: true,
-							contrast: true,
-							text: (
-								<Text id="app.special.modals.actions.create" />
-							),
-							onClick: async () => {
-								setProcessing(true);
+            return (
+                <PromptModal
+                    onClose={onClose}
+                    question={<Text id="app.context_menu.create_channel" />}
+                    actions={[
+                        {
+                            confirmation: true,
+                            contrast: true,
+                            text: (
+                                <Text id="app.special.modals.actions.create" />
+                            ),
+                            onClick: async () => {
+                                setProcessing(true);
 
-								try {
-									const channel =
-										await client.servers.createChannel(
-											props.target._id,
-											{
-												type,
-												name,
-												nonce: ulid(),
-											},
-										);
+                                try {
+                                    const channel =
+                                        await client.servers.createChannel(
+                                            props.target._id,
+                                            {
+                                                type,
+                                                name,
+                                                nonce: ulid(),
+                                            },
+                                        );
 
-									history.push(
-										`/server/${props.target._id}/channel/${channel._id}`,
-									);
-									onClose();
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							},
-						},
-						{
-							text: (
-								<Text id="app.special.modals.actions.cancel" />
-							),
-							onClick: onClose,
-						},
-					]}
-					content={
-						<>
-							<Overline block type="subtle">
-								<Text id="app.main.servers.channel_type" />
-							</Overline>
-							<Radio
-								checked={type === "Text"}
-								onSelect={() => setType("Text")}>
-								<Text id="app.main.servers.text_channel" />
-							</Radio>
-							<Radio
-								checked={type === "Voice"}
-								onSelect={() => setType("Voice")}>
-								<Text id="app.main.servers.voice_channel" />
-							</Radio>
-							<Overline block type="subtle">
-								<Text id="app.main.servers.channel_name" />
-							</Overline>
-							<InputBox
-								value={name}
-								onChange={(e) => setName(e.currentTarget.value)}
-							/>
-						</>
-					}
-					disabled={processing}
-					error={error}
-				/>
-			);
-		}
-		default:
-			return null;
-	}
+                                    history.push(
+                                        `/server/${props.target._id}/channel/${channel._id}`,
+                                    );
+                                    onClose();
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            },
+                        },
+                        {
+                            text: (
+                                <Text id="app.special.modals.actions.cancel" />
+                            ),
+                            onClick: onClose,
+                        },
+                    ]}
+                    content={
+                        <>
+                            <Overline block type="subtle">
+                                <Text id="app.main.servers.channel_type" />
+                            </Overline>
+                            <Radio
+                                checked={type === "Text"}
+                                onSelect={() => setType("Text")}>
+                                <Text id="app.main.servers.text_channel" />
+                            </Radio>
+                            <Radio
+                                checked={type === "Voice"}
+                                onSelect={() => setType("Voice")}>
+                                <Text id="app.main.servers.voice_channel" />
+                            </Radio>
+                            <Overline block type="subtle">
+                                <Text id="app.main.servers.channel_name" />
+                            </Overline>
+                            <InputBox
+                                value={name}
+                                onChange={(e) => setName(e.currentTarget.value)}
+                            />
+                        </>
+                    }
+                    disabled={processing}
+                    error={error}
+                />
+            );
+        }
+        default:
+            return null;
+    }
 }
diff --git a/src/context/intermediate/modals/SignedOut.tsx b/src/context/intermediate/modals/SignedOut.tsx
index e04ba10..7c7f357 100644
--- a/src/context/intermediate/modals/SignedOut.tsx
+++ b/src/context/intermediate/modals/SignedOut.tsx
@@ -3,22 +3,22 @@ import { Text } from "preact-i18n";
 import Modal from "../../../components/ui/Modal";
 
 interface Props {
-	onClose: () => void;
+    onClose: () => void;
 }
 
 export function SignedOutModal({ onClose }: Props) {
-	return (
-		<Modal
-			visible={true}
-			onClose={onClose}
-			title={<Text id="app.special.modals.signed_out" />}
-			actions={[
-				{
-					onClick: onClose,
-					confirmation: true,
-					text: <Text id="app.special.modals.actions.ok" />,
-				},
-			]}
-		/>
-	);
+    return (
+        <Modal
+            visible={true}
+            onClose={onClose}
+            title={<Text id="app.special.modals.signed_out" />}
+            actions={[
+                {
+                    onClick: onClose,
+                    confirmation: true,
+                    text: <Text id="app.special.modals.actions.ok" />,
+                },
+            ]}
+        />
+    );
 }
diff --git a/src/context/intermediate/popovers/ChannelInfo.tsx b/src/context/intermediate/popovers/ChannelInfo.tsx
index f10c145..18a492a 100644
--- a/src/context/intermediate/popovers/ChannelInfo.tsx
+++ b/src/context/intermediate/popovers/ChannelInfo.tsx
@@ -9,36 +9,36 @@ import { useChannel, useForceUpdate } from "../../revoltjs/hooks";
 import { getChannelName } from "../../revoltjs/util";
 
 interface Props {
-	channel_id: string;
-	onClose: () => void;
+    channel_id: string;
+    onClose: () => void;
 }
 
 export function ChannelInfo({ channel_id, onClose }: Props) {
-	const ctx = useForceUpdate();
-	const channel = useChannel(channel_id, ctx);
-	if (!channel) return null;
+    const ctx = useForceUpdate();
+    const channel = useChannel(channel_id, ctx);
+    if (!channel) return null;
 
-	if (
-		channel.channel_type === "DirectMessage" ||
-		channel.channel_type === "SavedMessages"
-	) {
-		onClose();
-		return null;
-	}
+    if (
+        channel.channel_type === "DirectMessage" ||
+        channel.channel_type === "SavedMessages"
+    ) {
+        onClose();
+        return null;
+    }
 
-	return (
-		<Modal visible={true} onClose={onClose}>
-			<div className={styles.info}>
-				<div className={styles.header}>
-					<h1>{getChannelName(ctx.client, channel, true)}</h1>
-					<div onClick={onClose}>
-						<X size={36} />
-					</div>
-				</div>
-				<p>
-					<Markdown content={channel.description} />
-				</p>
-			</div>
-		</Modal>
-	);
+    return (
+        <Modal visible={true} onClose={onClose}>
+            <div className={styles.info}>
+                <div className={styles.header}>
+                    <h1>{getChannelName(ctx.client, channel, true)}</h1>
+                    <div onClick={onClose}>
+                        <X size={36} />
+                    </div>
+                </div>
+                <p>
+                    <Markdown content={channel.description} />
+                </p>
+            </div>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/popovers/ImageViewer.tsx b/src/context/intermediate/popovers/ImageViewer.tsx
index a199ba7..ca4f6ba 100644
--- a/src/context/intermediate/popovers/ImageViewer.tsx
+++ b/src/context/intermediate/popovers/ImageViewer.tsx
@@ -10,37 +10,37 @@ import Modal from "../../../components/ui/Modal";
 import { AppContext } from "../../revoltjs/RevoltClient";
 
 interface Props {
-	onClose: () => void;
-	embed?: EmbedImage;
-	attachment?: Attachment;
+    onClose: () => void;
+    embed?: EmbedImage;
+    attachment?: Attachment;
 }
 
 export function ImageViewer({ attachment, embed, onClose }: Props) {
-	// ! FIXME: temp code
-	// ! add proxy function to client
-	function proxyImage(url: string) {
-		return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
-	}
+    // ! FIXME: temp code
+    // ! add proxy function to client
+    function proxyImage(url: string) {
+        return "https://jan.revolt.chat/proxy?url=" + encodeURIComponent(url);
+    }
 
-	if (attachment && attachment.metadata.type !== "Image") return null;
-	const client = useContext(AppContext);
+    if (attachment && attachment.metadata.type !== "Image") return null;
+    const client = useContext(AppContext);
 
-	return (
-		<Modal visible={true} onClose={onClose} noBackground>
-			<div className={styles.viewer}>
-				{attachment && (
-					<>
-						<img src={client.generateFileURL(attachment)} />
-						<AttachmentActions attachment={attachment} />
-					</>
-				)}
-				{embed && (
-					<>
-						<img src={proxyImage(embed.url)} />
-						<EmbedMediaActions embed={embed} />
-					</>
-				)}
-			</div>
-		</Modal>
-	);
+    return (
+        <Modal visible={true} onClose={onClose} noBackground>
+            <div className={styles.viewer}>
+                {attachment && (
+                    <>
+                        <img src={client.generateFileURL(attachment)} />
+                        <AttachmentActions attachment={attachment} />
+                    </>
+                )}
+                {embed && (
+                    <>
+                        <img src={proxyImage(embed.url)} />
+                        <EmbedMediaActions embed={embed} />
+                    </>
+                )}
+            </div>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/popovers/ModifyAccount.tsx b/src/context/intermediate/popovers/ModifyAccount.tsx
index b719554..2b8aaf2 100644
--- a/src/context/intermediate/popovers/ModifyAccount.tsx
+++ b/src/context/intermediate/popovers/ModifyAccount.tsx
@@ -11,124 +11,124 @@ import { AppContext } from "../../revoltjs/RevoltClient";
 import { takeError } from "../../revoltjs/util";
 
 interface Props {
-	onClose: () => void;
-	field: "username" | "email" | "password";
+    onClose: () => void;
+    field: "username" | "email" | "password";
 }
 
 interface FormInputs {
-	password: string;
-	new_email: string;
-	new_username: string;
-	new_password: string;
+    password: string;
+    new_email: string;
+    new_username: string;
+    new_password: string;
 
-	// TODO: figure out if this is correct or not
-	// it wasn't in the types before this was typed but the element itself was there
-	current_password?: string;
+    // TODO: figure out if this is correct or not
+    // it wasn't in the types before this was typed but the element itself was there
+    current_password?: string;
 }
 
 export function ModifyAccountModal({ onClose, field }: Props) {
-	const client = useContext(AppContext);
-	const { handleSubmit, register, errors } = useForm<FormInputs>();
-	const [error, setError] = useState<string | undefined>(undefined);
+    const client = useContext(AppContext);
+    const { handleSubmit, register, errors } = useForm<FormInputs>();
+    const [error, setError] = useState<string | undefined>(undefined);
 
-	const onSubmit: SubmitHandler<FormInputs> = async ({
-		password,
-		new_username,
-		new_email,
-		new_password,
-	}) => {
-		try {
-			if (field === "email") {
-				await client.req("POST", "/auth/change/email", {
-					password,
-					new_email,
-				});
-				onClose();
-			} else if (field === "password") {
-				await client.req("POST", "/auth/change/password", {
-					password,
-					new_password,
-				});
-				onClose();
-			} else if (field === "username") {
-				await client.req("PATCH", "/users/id/username", {
-					username: new_username,
-					password,
-				});
-				onClose();
-			}
-		} catch (err) {
-			setError(takeError(err));
-		}
-	};
+    const onSubmit: SubmitHandler<FormInputs> = async ({
+        password,
+        new_username,
+        new_email,
+        new_password,
+    }) => {
+        try {
+            if (field === "email") {
+                await client.req("POST", "/auth/change/email", {
+                    password,
+                    new_email,
+                });
+                onClose();
+            } else if (field === "password") {
+                await client.req("POST", "/auth/change/password", {
+                    password,
+                    new_password,
+                });
+                onClose();
+            } else if (field === "username") {
+                await client.req("PATCH", "/users/id/username", {
+                    username: new_username,
+                    password,
+                });
+                onClose();
+            }
+        } catch (err) {
+            setError(takeError(err));
+        }
+    };
 
-	return (
-		<Modal
-			visible={true}
-			onClose={onClose}
-			title={<Text id={`app.special.modals.account.change.${field}`} />}
-			actions={[
-				{
-					confirmation: true,
-					onClick: handleSubmit(onSubmit),
-					text:
-						field === "email" ? (
-							<Text id="app.special.modals.actions.send_email" />
-						) : (
-							<Text id="app.special.modals.actions.update" />
-						),
-				},
-				{
-					onClick: onClose,
-					text: <Text id="app.special.modals.actions.close" />,
-				},
-			]}>
-			{/* Preact / React typing incompatabilities */}
-			<form
-				onSubmit={
-					handleSubmit(
-						onSubmit,
-					) as JSX.GenericEventHandler<HTMLFormElement>
-				}>
-				{field === "email" && (
-					<FormField
-						type="email"
-						name="new_email"
-						register={register}
-						showOverline
-						error={errors.new_email?.message}
-					/>
-				)}
-				{field === "password" && (
-					<FormField
-						type="password"
-						name="new_password"
-						register={register}
-						showOverline
-						error={errors.new_password?.message}
-					/>
-				)}
-				{field === "username" && (
-					<FormField
-						type="username"
-						name="new_username"
-						register={register}
-						showOverline
-						error={errors.new_username?.message}
-					/>
-				)}
-				<FormField
-					type="current_password"
-					register={register}
-					showOverline
-					error={errors.current_password?.message}
-				/>
-				{error && (
-					<Overline type="error" error={error}>
-						<Text id="app.special.modals.account.failed" />
-					</Overline>
-				)}
-			</form>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            onClose={onClose}
+            title={<Text id={`app.special.modals.account.change.${field}`} />}
+            actions={[
+                {
+                    confirmation: true,
+                    onClick: handleSubmit(onSubmit),
+                    text:
+                        field === "email" ? (
+                            <Text id="app.special.modals.actions.send_email" />
+                        ) : (
+                            <Text id="app.special.modals.actions.update" />
+                        ),
+                },
+                {
+                    onClick: onClose,
+                    text: <Text id="app.special.modals.actions.close" />,
+                },
+            ]}>
+            {/* Preact / React typing incompatabilities */}
+            <form
+                onSubmit={
+                    handleSubmit(
+                        onSubmit,
+                    ) as JSX.GenericEventHandler<HTMLFormElement>
+                }>
+                {field === "email" && (
+                    <FormField
+                        type="email"
+                        name="new_email"
+                        register={register}
+                        showOverline
+                        error={errors.new_email?.message}
+                    />
+                )}
+                {field === "password" && (
+                    <FormField
+                        type="password"
+                        name="new_password"
+                        register={register}
+                        showOverline
+                        error={errors.new_password?.message}
+                    />
+                )}
+                {field === "username" && (
+                    <FormField
+                        type="username"
+                        name="new_username"
+                        register={register}
+                        showOverline
+                        error={errors.new_username?.message}
+                    />
+                )}
+                <FormField
+                    type="current_password"
+                    register={register}
+                    showOverline
+                    error={errors.current_password?.message}
+                />
+                {error && (
+                    <Overline type="error" error={error}>
+                        <Text id="app.special.modals.account.failed" />
+                    </Overline>
+                )}
+            </form>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/popovers/PendingRequests.tsx b/src/context/intermediate/popovers/PendingRequests.tsx
index 424e326..896958a 100644
--- a/src/context/intermediate/popovers/PendingRequests.tsx
+++ b/src/context/intermediate/popovers/PendingRequests.tsx
@@ -7,25 +7,25 @@ import { Friend } from "../../../pages/friends/Friend";
 import { useUsers } from "../../revoltjs/hooks";
 
 interface Props {
-	users: string[];
-	onClose: () => void;
+    users: string[];
+    onClose: () => void;
 }
 
 export function PendingRequests({ users: ids, onClose }: Props) {
-	const users = useUsers(ids);
+    const users = useUsers(ids);
 
-	return (
-		<Modal
-			visible={true}
-			title={<Text id="app.special.friends.pending" />}
-			onClose={onClose}>
-			<div className={styles.list}>
-				{users
-					.filter((x) => typeof x !== "undefined")
-					.map((x) => (
-						<Friend user={x!} key={x!._id} />
-					))}
-			</div>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            title={<Text id="app.special.friends.pending" />}
+            onClose={onClose}>
+            <div className={styles.list}>
+                {users
+                    .filter((x) => typeof x !== "undefined")
+                    .map((x) => (
+                        <Friend user={x!} key={x!._id} />
+                    ))}
+            </div>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/popovers/UserPicker.tsx b/src/context/intermediate/popovers/UserPicker.tsx
index 09f8e40..0dc0792 100644
--- a/src/context/intermediate/popovers/UserPicker.tsx
+++ b/src/context/intermediate/popovers/UserPicker.tsx
@@ -10,59 +10,59 @@ import Modal from "../../../components/ui/Modal";
 import { useUsers } from "../../revoltjs/hooks";
 
 interface Props {
-	omit?: string[];
-	onClose: () => void;
-	callback: (users: string[]) => Promise<void>;
+    omit?: string[];
+    onClose: () => void;
+    callback: (users: string[]) => Promise<void>;
 }
 
 export function UserPicker(props: Props) {
-	const [selected, setSelected] = useState<string[]>([]);
-	const omit = [...(props.omit || []), "00000000000000000000000000"];
+    const [selected, setSelected] = useState<string[]>([]);
+    const omit = [...(props.omit || []), "00000000000000000000000000"];
 
-	const users = useUsers();
+    const users = useUsers();
 
-	return (
-		<Modal
-			visible={true}
-			title={<Text id="app.special.popovers.user_picker.select" />}
-			onClose={props.onClose}
-			actions={[
-				{
-					text: <Text id="app.special.modals.actions.ok" />,
-					onClick: () => props.callback(selected).then(props.onClose),
-				},
-			]}>
-			<div className={styles.list}>
-				{(
-					users.filter(
-						(x) =>
-							x &&
-							x.relationship === Users.Relationship.Friend &&
-							!omit.includes(x._id),
-					) as User[]
-				)
-					.map((x) => {
-						return {
-							...x,
-							selected: selected.includes(x._id),
-						};
-					})
-					.map((x) => (
-						<UserCheckbox
-							user={x}
-							checked={x.selected}
-							onChange={(v) => {
-								if (v) {
-									setSelected([...selected, x._id]);
-								} else {
-									setSelected(
-										selected.filter((y) => y !== x._id),
-									);
-								}
-							}}
-						/>
-					))}
-			</div>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible={true}
+            title={<Text id="app.special.popovers.user_picker.select" />}
+            onClose={props.onClose}
+            actions={[
+                {
+                    text: <Text id="app.special.modals.actions.ok" />,
+                    onClick: () => props.callback(selected).then(props.onClose),
+                },
+            ]}>
+            <div className={styles.list}>
+                {(
+                    users.filter(
+                        (x) =>
+                            x &&
+                            x.relationship === Users.Relationship.Friend &&
+                            !omit.includes(x._id),
+                    ) as User[]
+                )
+                    .map((x) => {
+                        return {
+                            ...x,
+                            selected: selected.includes(x._id),
+                        };
+                    })
+                    .map((x) => (
+                        <UserCheckbox
+                            user={x}
+                            checked={x.selected}
+                            onChange={(v) => {
+                                if (v) {
+                                    setSelected([...selected, x._id]);
+                                } else {
+                                    setSelected(
+                                        selected.filter((y) => y !== x._id),
+                                    );
+                                }
+                            }}
+                        />
+                    ))}
+            </div>
+        </Modal>
+    );
 }
diff --git a/src/context/intermediate/popovers/UserProfile.tsx b/src/context/intermediate/popovers/UserProfile.tsx
index beb2833..653a6ee 100644
--- a/src/context/intermediate/popovers/UserProfile.tsx
+++ b/src/context/intermediate/popovers/UserProfile.tsx
@@ -1,9 +1,9 @@
 import {
-	Envelope,
-	Edit,
-	UserPlus,
-	Shield,
-	Money,
+    Envelope,
+    Edit,
+    UserPlus,
+    Shield,
+    Money,
 } from "@styled-icons/boxicons-regular";
 import { Link, useHistory } from "react-router-dom";
 import { Users } from "revolt.js/dist/api/objects";
@@ -25,338 +25,338 @@ import Preloader from "../../../components/ui/Preloader";
 
 import Markdown from "../../../components/markdown/Markdown";
 import {
-	AppContext,
-	ClientStatus,
-	StatusContext,
+    AppContext,
+    ClientStatus,
+    StatusContext,
 } from "../../revoltjs/RevoltClient";
 import {
-	useChannels,
-	useForceUpdate,
-	useUserPermission,
-	useUsers,
+    useChannels,
+    useForceUpdate,
+    useUserPermission,
+    useUsers,
 } from "../../revoltjs/hooks";
 import { useIntermediate } from "../Intermediate";
 
 interface Props {
-	user_id: string;
-	dummy?: boolean;
-	onClose: () => void;
-	dummyProfile?: Users.Profile;
+    user_id: string;
+    dummy?: boolean;
+    onClose: () => void;
+    dummyProfile?: Users.Profile;
 }
 
 enum Badges {
-	Developer = 1,
-	Translator = 2,
-	Supporter = 4,
-	ResponsibleDisclosure = 8,
-	EarlyAdopter = 256,
+    Developer = 1,
+    Translator = 2,
+    Supporter = 4,
+    ResponsibleDisclosure = 8,
+    EarlyAdopter = 256,
 }
 
 export function UserProfile({ user_id, onClose, dummy, dummyProfile }: Props) {
-	const { openScreen, writeClipboard } = useIntermediate();
+    const { openScreen, writeClipboard } = useIntermediate();
 
-	const [profile, setProfile] = useState<undefined | null | Users.Profile>(
-		undefined,
-	);
-	const [mutual, setMutual] = useState<
-		undefined | null | Route<"GET", "/users/id/mutual">["response"]
-	>(undefined);
+    const [profile, setProfile] = useState<undefined | null | Users.Profile>(
+        undefined,
+    );
+    const [mutual, setMutual] = useState<
+        undefined | null | Route<"GET", "/users/id/mutual">["response"]
+    >(undefined);
 
-	const history = useHistory();
-	const client = useContext(AppContext);
-	const status = useContext(StatusContext);
-	const [tab, setTab] = useState("profile");
+    const history = useHistory();
+    const client = useContext(AppContext);
+    const status = useContext(StatusContext);
+    const [tab, setTab] = useState("profile");
 
-	const ctx = useForceUpdate();
-	const all_users = useUsers(undefined, ctx);
-	const channels = useChannels(undefined, ctx);
+    const ctx = useForceUpdate();
+    const all_users = useUsers(undefined, ctx);
+    const channels = useChannels(undefined, ctx);
 
-	const user = all_users.find((x) => x!._id === user_id);
-	const users = mutual?.users
-		? all_users.filter((x) => mutual.users.includes(x!._id))
-		: undefined;
+    const user = all_users.find((x) => x!._id === user_id);
+    const users = mutual?.users
+        ? all_users.filter((x) => mutual.users.includes(x!._id))
+        : undefined;
 
-	if (!user) {
-		useEffect(onClose, []);
-		return null;
-	}
+    if (!user) {
+        useEffect(onClose, []);
+        return null;
+    }
 
-	const permissions = useUserPermission(user!._id, ctx);
+    const permissions = useUserPermission(user!._id, ctx);
 
-	useLayoutEffect(() => {
-		if (!user_id) return;
-		if (typeof profile !== "undefined") setProfile(undefined);
-		if (typeof mutual !== "undefined") setMutual(undefined);
-	}, [user_id]);
+    useLayoutEffect(() => {
+        if (!user_id) return;
+        if (typeof profile !== "undefined") setProfile(undefined);
+        if (typeof mutual !== "undefined") setMutual(undefined);
+    }, [user_id]);
 
-	if (dummy) {
-		useLayoutEffect(() => {
-			setProfile(dummyProfile);
-		}, [dummyProfile]);
-	}
+    if (dummy) {
+        useLayoutEffect(() => {
+            setProfile(dummyProfile);
+        }, [dummyProfile]);
+    }
 
-	useEffect(() => {
-		if (dummy) return;
-		if (status === ClientStatus.ONLINE && typeof mutual === "undefined") {
-			setMutual(null);
-			client.users.fetchMutual(user_id).then((data) => setMutual(data));
-		}
-	}, [mutual, status]);
+    useEffect(() => {
+        if (dummy) return;
+        if (status === ClientStatus.ONLINE && typeof mutual === "undefined") {
+            setMutual(null);
+            client.users.fetchMutual(user_id).then((data) => setMutual(data));
+        }
+    }, [mutual, status]);
 
-	useEffect(() => {
-		if (dummy) return;
-		if (status === ClientStatus.ONLINE && typeof profile === "undefined") {
-			setProfile(null);
+    useEffect(() => {
+        if (dummy) return;
+        if (status === ClientStatus.ONLINE && typeof profile === "undefined") {
+            setProfile(null);
 
-			if (permissions & UserPermission.ViewProfile) {
-				client.users
-					.fetchProfile(user_id)
-					.then((data) => setProfile(data))
-					.catch(() => {});
-			}
-		}
-	}, [profile, status]);
+            if (permissions & UserPermission.ViewProfile) {
+                client.users
+                    .fetchProfile(user_id)
+                    .then((data) => setProfile(data))
+                    .catch(() => {});
+            }
+        }
+    }, [profile, status]);
 
-	const mutualGroups = channels.filter(
-		(channel) =>
-			channel?.channel_type === "Group" &&
-			channel.recipients.includes(user_id),
-	);
+    const mutualGroups = channels.filter(
+        (channel) =>
+            channel?.channel_type === "Group" &&
+            channel.recipients.includes(user_id),
+    );
 
-	const backgroundURL =
-		profile &&
-		client.users.getBackgroundURL(profile, { width: 1000 }, true);
-	const badges =
-		(user.badges ?? 0) |
-		(decodeTime(user._id) < 1623751765790 ? Badges.EarlyAdopter : 0);
+    const backgroundURL =
+        profile &&
+        client.users.getBackgroundURL(profile, { width: 1000 }, true);
+    const badges =
+        (user.badges ?? 0) |
+        (decodeTime(user._id) < 1623751765790 ? Badges.EarlyAdopter : 0);
 
-	return (
-		<Modal
-			visible
-			border={dummy}
-			padding={false}
-			onClose={onClose}
-			dontModal={dummy}>
-			<div
-				className={styles.header}
-				data-force={profile?.background ? "light" : undefined}
-				style={{
-					backgroundImage:
-						backgroundURL &&
-						`linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`,
-				}}>
-				<div className={styles.profile}>
-					<UserIcon size={80} target={user} status />
-					<div className={styles.details}>
-						<Localizer>
-							<span
-								className={styles.username}
-								onClick={() => writeClipboard(user.username)}>
-								@{user.username}
-							</span>
-						</Localizer>
-						{user.status?.text && (
-							<span className={styles.status}>
-								<UserStatus user={user} />
-							</span>
-						)}
-					</div>
-					{user.relationship === Users.Relationship.Friend && (
-						<Localizer>
-							<Tooltip
-								content={
-									<Text id="app.context_menu.message_user" />
-								}>
-								<IconButton
-									onClick={() => {
-										onClose();
-										history.push(`/open/${user_id}`);
-									}}>
-									<Envelope size={30} />
-								</IconButton>
-							</Tooltip>
-						</Localizer>
-					)}
-					{user.relationship === Users.Relationship.User && (
-						<IconButton
-							onClick={() => {
-								onClose();
-								if (dummy) return;
-								history.push(`/settings/profile`);
-							}}>
-							<Edit size={28} />
-						</IconButton>
-					)}
-					{(user.relationship === Users.Relationship.Incoming ||
-						user.relationship === Users.Relationship.None) && (
-						<IconButton
-							onClick={() =>
-								client.users.addFriend(user.username)
-							}>
-							<UserPlus size={28} />
-						</IconButton>
-					)}
-				</div>
-				<div className={styles.tabs}>
-					<div
-						data-active={tab === "profile"}
-						onClick={() => setTab("profile")}>
-						<Text id="app.special.popovers.user_profile.profile" />
-					</div>
-					{user.relationship !== Users.Relationship.User && (
-						<>
-							<div
-								data-active={tab === "friends"}
-								onClick={() => setTab("friends")}>
-								<Text id="app.special.popovers.user_profile.mutual_friends" />
-							</div>
-							<div
-								data-active={tab === "groups"}
-								onClick={() => setTab("groups")}>
-								<Text id="app.special.popovers.user_profile.mutual_groups" />
-							</div>
-						</>
-					)}
-				</div>
-			</div>
-			<div className={styles.content}>
-				{tab === "profile" && (
-					<div>
-						{!(profile?.content || badges > 0) && (
-							<div className={styles.empty}>
-								<Text id="app.special.popovers.user_profile.empty" />
-							</div>
-						)}
-						{badges > 0 && (
-							<div className={styles.category}>
-								<Text id="app.special.popovers.user_profile.sub.badges" />
-							</div>
-						)}
-						{badges > 0 && (
-							<div className={styles.badges}>
-								<Localizer>
-									{badges & Badges.Developer ? (
-										<Tooltip
-											content={
-												<Text id="app.navigation.tabs.dev" />
-											}>
-											<img src="/assets/badges/developer.svg" />
-										</Tooltip>
-									) : (
-										<></>
-									)}
-									{badges & Badges.Translator ? (
-										<Tooltip
-											content={
-												<Text id="app.special.popovers.user_profile.badges.translator" />
-											}>
-											<img src="/assets/badges/translator.svg" />
-										</Tooltip>
-									) : (
-										<></>
-									)}
-									{badges & Badges.EarlyAdopter ? (
-										<Tooltip
-											content={
-												<Text id="app.special.popovers.user_profile.badges.early_adopter" />
-											}>
-											<img src="/assets/badges/early_adopter.svg" />
-										</Tooltip>
-									) : (
-										<></>
-									)}
-									{badges & Badges.Supporter ? (
-										<Tooltip
-											content={
-												<Text id="app.special.popovers.user_profile.badges.supporter" />
-											}>
-											<Money size={32} color="#efab44" />
-										</Tooltip>
-									) : (
-										<></>
-									)}
-									{badges & Badges.ResponsibleDisclosure ? (
-										<Tooltip
-											content={
-												<Text id="app.special.popovers.user_profile.badges.responsible_disclosure" />
-											}>
-											<Shield size={32} color="gray" />
-										</Tooltip>
-									) : (
-										<></>
-									)}
-								</Localizer>
-							</div>
-						)}
-						{profile?.content && (
-							<div className={styles.category}>
-								<Text id="app.special.popovers.user_profile.sub.information" />
-							</div>
-						)}
-						<Markdown content={profile?.content} />
-						{/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
-					</div>
-				)}
-				{tab === "friends" &&
-					(users ? (
-						<div className={styles.entries}>
-							{users.length === 0 ? (
-								<div className={styles.empty}>
-									<Text id="app.special.popovers.user_profile.no_users" />
-								</div>
-							) : (
-								users.map(
-									(x) =>
-										x && (
-											<div
-												onClick={() =>
-													openScreen({
-														id: "profile",
-														user_id: x._id,
-													})
-												}
-												className={styles.entry}
-												key={x._id}>
-												<UserIcon
-													size={32}
-													target={x}
-												/>
-												<span>{x.username}</span>
-											</div>
-										),
-								)
-							)}
-						</div>
-					) : (
-						<Preloader type="ring" />
-					))}
-				{tab === "groups" && (
-					<div className={styles.entries}>
-						{mutualGroups.length === 0 ? (
-							<div className={styles.empty}>
-								<Text id="app.special.popovers.user_profile.no_groups" />
-							</div>
-						) : (
-							mutualGroups.map(
-								(x) =>
-									x?.channel_type === "Group" && (
-										<Link to={`/channel/${x._id}`}>
-											<div
-												className={styles.entry}
-												key={x._id}>
-												<ChannelIcon
-													target={x}
-													size={32}
-												/>
-												<span>{x.name}</span>
-											</div>
-										</Link>
-									),
-							)
-						)}
-					</div>
-				)}
-			</div>
-		</Modal>
-	);
+    return (
+        <Modal
+            visible
+            border={dummy}
+            padding={false}
+            onClose={onClose}
+            dontModal={dummy}>
+            <div
+                className={styles.header}
+                data-force={profile?.background ? "light" : undefined}
+                style={{
+                    backgroundImage:
+                        backgroundURL &&
+                        `linear-gradient( rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7) ), url('${backgroundURL}')`,
+                }}>
+                <div className={styles.profile}>
+                    <UserIcon size={80} target={user} status />
+                    <div className={styles.details}>
+                        <Localizer>
+                            <span
+                                className={styles.username}
+                                onClick={() => writeClipboard(user.username)}>
+                                @{user.username}
+                            </span>
+                        </Localizer>
+                        {user.status?.text && (
+                            <span className={styles.status}>
+                                <UserStatus user={user} />
+                            </span>
+                        )}
+                    </div>
+                    {user.relationship === Users.Relationship.Friend && (
+                        <Localizer>
+                            <Tooltip
+                                content={
+                                    <Text id="app.context_menu.message_user" />
+                                }>
+                                <IconButton
+                                    onClick={() => {
+                                        onClose();
+                                        history.push(`/open/${user_id}`);
+                                    }}>
+                                    <Envelope size={30} />
+                                </IconButton>
+                            </Tooltip>
+                        </Localizer>
+                    )}
+                    {user.relationship === Users.Relationship.User && (
+                        <IconButton
+                            onClick={() => {
+                                onClose();
+                                if (dummy) return;
+                                history.push(`/settings/profile`);
+                            }}>
+                            <Edit size={28} />
+                        </IconButton>
+                    )}
+                    {(user.relationship === Users.Relationship.Incoming ||
+                        user.relationship === Users.Relationship.None) && (
+                        <IconButton
+                            onClick={() =>
+                                client.users.addFriend(user.username)
+                            }>
+                            <UserPlus size={28} />
+                        </IconButton>
+                    )}
+                </div>
+                <div className={styles.tabs}>
+                    <div
+                        data-active={tab === "profile"}
+                        onClick={() => setTab("profile")}>
+                        <Text id="app.special.popovers.user_profile.profile" />
+                    </div>
+                    {user.relationship !== Users.Relationship.User && (
+                        <>
+                            <div
+                                data-active={tab === "friends"}
+                                onClick={() => setTab("friends")}>
+                                <Text id="app.special.popovers.user_profile.mutual_friends" />
+                            </div>
+                            <div
+                                data-active={tab === "groups"}
+                                onClick={() => setTab("groups")}>
+                                <Text id="app.special.popovers.user_profile.mutual_groups" />
+                            </div>
+                        </>
+                    )}
+                </div>
+            </div>
+            <div className={styles.content}>
+                {tab === "profile" && (
+                    <div>
+                        {!(profile?.content || badges > 0) && (
+                            <div className={styles.empty}>
+                                <Text id="app.special.popovers.user_profile.empty" />
+                            </div>
+                        )}
+                        {badges > 0 && (
+                            <div className={styles.category}>
+                                <Text id="app.special.popovers.user_profile.sub.badges" />
+                            </div>
+                        )}
+                        {badges > 0 && (
+                            <div className={styles.badges}>
+                                <Localizer>
+                                    {badges & Badges.Developer ? (
+                                        <Tooltip
+                                            content={
+                                                <Text id="app.navigation.tabs.dev" />
+                                            }>
+                                            <img src="/assets/badges/developer.svg" />
+                                        </Tooltip>
+                                    ) : (
+                                        <></>
+                                    )}
+                                    {badges & Badges.Translator ? (
+                                        <Tooltip
+                                            content={
+                                                <Text id="app.special.popovers.user_profile.badges.translator" />
+                                            }>
+                                            <img src="/assets/badges/translator.svg" />
+                                        </Tooltip>
+                                    ) : (
+                                        <></>
+                                    )}
+                                    {badges & Badges.EarlyAdopter ? (
+                                        <Tooltip
+                                            content={
+                                                <Text id="app.special.popovers.user_profile.badges.early_adopter" />
+                                            }>
+                                            <img src="/assets/badges/early_adopter.svg" />
+                                        </Tooltip>
+                                    ) : (
+                                        <></>
+                                    )}
+                                    {badges & Badges.Supporter ? (
+                                        <Tooltip
+                                            content={
+                                                <Text id="app.special.popovers.user_profile.badges.supporter" />
+                                            }>
+                                            <Money size={32} color="#efab44" />
+                                        </Tooltip>
+                                    ) : (
+                                        <></>
+                                    )}
+                                    {badges & Badges.ResponsibleDisclosure ? (
+                                        <Tooltip
+                                            content={
+                                                <Text id="app.special.popovers.user_profile.badges.responsible_disclosure" />
+                                            }>
+                                            <Shield size={32} color="gray" />
+                                        </Tooltip>
+                                    ) : (
+                                        <></>
+                                    )}
+                                </Localizer>
+                            </div>
+                        )}
+                        {profile?.content && (
+                            <div className={styles.category}>
+                                <Text id="app.special.popovers.user_profile.sub.information" />
+                            </div>
+                        )}
+                        <Markdown content={profile?.content} />
+                        {/*<div className={styles.category}><Text id="app.special.popovers.user_profile.sub.connections" /></div>*/}
+                    </div>
+                )}
+                {tab === "friends" &&
+                    (users ? (
+                        <div className={styles.entries}>
+                            {users.length === 0 ? (
+                                <div className={styles.empty}>
+                                    <Text id="app.special.popovers.user_profile.no_users" />
+                                </div>
+                            ) : (
+                                users.map(
+                                    (x) =>
+                                        x && (
+                                            <div
+                                                onClick={() =>
+                                                    openScreen({
+                                                        id: "profile",
+                                                        user_id: x._id,
+                                                    })
+                                                }
+                                                className={styles.entry}
+                                                key={x._id}>
+                                                <UserIcon
+                                                    size={32}
+                                                    target={x}
+                                                />
+                                                <span>{x.username}</span>
+                                            </div>
+                                        ),
+                                )
+                            )}
+                        </div>
+                    ) : (
+                        <Preloader type="ring" />
+                    ))}
+                {tab === "groups" && (
+                    <div className={styles.entries}>
+                        {mutualGroups.length === 0 ? (
+                            <div className={styles.empty}>
+                                <Text id="app.special.popovers.user_profile.no_groups" />
+                            </div>
+                        ) : (
+                            mutualGroups.map(
+                                (x) =>
+                                    x?.channel_type === "Group" && (
+                                        <Link to={`/channel/${x._id}`}>
+                                            <div
+                                                className={styles.entry}
+                                                key={x._id}>
+                                                <ChannelIcon
+                                                    target={x}
+                                                    size={32}
+                                                />
+                                                <span>{x.name}</span>
+                                            </div>
+                                        </Link>
+                                    ),
+                            )
+                        )}
+                    </div>
+                )}
+            </div>
+        </Modal>
+    );
 }
diff --git a/src/context/revoltjs/CheckAuth.tsx b/src/context/revoltjs/CheckAuth.tsx
index 0865a78..943c2e4 100644
--- a/src/context/revoltjs/CheckAuth.tsx
+++ b/src/context/revoltjs/CheckAuth.tsx
@@ -6,18 +6,18 @@ import { Children } from "../../types/Preact";
 import { OperationsContext } from "./RevoltClient";
 
 interface Props {
-	auth?: boolean;
-	children: Children;
+    auth?: boolean;
+    children: Children;
 }
 
 export const CheckAuth = (props: Props) => {
-	const operations = useContext(OperationsContext);
+    const operations = useContext(OperationsContext);
 
-	if (props.auth && !operations.ready()) {
-		return <Redirect to="/login" />;
-	} else if (!props.auth && operations.ready()) {
-		return <Redirect to="/" />;
-	}
+    if (props.auth && !operations.ready()) {
+        return <Redirect to="/login" />;
+    } else if (!props.auth && operations.ready()) {
+        return <Redirect to="/" />;
+    }
 
-	return <>{props.children}</>;
+    return <>{props.children}</>;
 };
diff --git a/src/context/revoltjs/FileUploads.tsx b/src/context/revoltjs/FileUploads.tsx
index 14b55f2..1aba031 100644
--- a/src/context/revoltjs/FileUploads.tsx
+++ b/src/context/revoltjs/FileUploads.tsx
@@ -17,276 +17,276 @@ import { AppContext } from "./RevoltClient";
 import { takeError } from "./util";
 
 type Props = {
-	maxFileSize: number;
-	remove: () => Promise<void>;
-	fileType: "backgrounds" | "icons" | "avatars" | "attachments" | "banners";
+    maxFileSize: number;
+    remove: () => Promise<void>;
+    fileType: "backgrounds" | "icons" | "avatars" | "attachments" | "banners";
 } & (
-	| { behaviour: "ask"; onChange: (file: File) => void }
-	| { behaviour: "upload"; onUpload: (id: string) => Promise<void> }
-	| {
-			behaviour: "multi";
-			onChange: (files: File[]) => void;
-			append?: (files: File[]) => void;
-	  }
+    | { behaviour: "ask"; onChange: (file: File) => void }
+    | { behaviour: "upload"; onUpload: (id: string) => Promise<void> }
+    | {
+          behaviour: "multi";
+          onChange: (files: File[]) => void;
+          append?: (files: File[]) => void;
+      }
 ) &
-	(
-		| {
-				style: "icon" | "banner";
-				defaultPreview?: string;
-				previewURL?: string;
-				width?: number;
-				height?: number;
-		  }
-		| {
-				style: "attachment";
-				attached: boolean;
-				uploading: boolean;
-				cancel: () => void;
-				size?: number;
-		  }
-	);
+    (
+        | {
+              style: "icon" | "banner";
+              defaultPreview?: string;
+              previewURL?: string;
+              width?: number;
+              height?: number;
+          }
+        | {
+              style: "attachment";
+              attached: boolean;
+              uploading: boolean;
+              cancel: () => void;
+              size?: number;
+          }
+    );
 
 export async function uploadFile(
-	autumnURL: string,
-	tag: string,
-	file: File,
-	config?: AxiosRequestConfig,
+    autumnURL: string,
+    tag: string,
+    file: File,
+    config?: AxiosRequestConfig,
 ) {
-	const formData = new FormData();
-	formData.append("file", file);
+    const formData = new FormData();
+    formData.append("file", file);
 
-	const res = await Axios.post(autumnURL + "/" + tag, formData, {
-		headers: {
-			"Content-Type": "multipart/form-data",
-		},
-		...config,
-	});
+    const res = await Axios.post(autumnURL + "/" + tag, formData, {
+        headers: {
+            "Content-Type": "multipart/form-data",
+        },
+        ...config,
+    });
 
-	return res.data.id;
+    return res.data.id;
 }
 
 export function grabFiles(
-	maxFileSize: number,
-	cb: (files: File[]) => void,
-	tooLarge: () => void,
-	multiple?: boolean,
+    maxFileSize: number,
+    cb: (files: File[]) => void,
+    tooLarge: () => void,
+    multiple?: boolean,
 ) {
-	const input = document.createElement("input");
-	input.type = "file";
-	input.multiple = multiple ?? false;
-
-	input.onchange = async (e) => {
-		const files = (e.currentTarget as HTMLInputElement)?.files;
-		if (!files) return;
-		for (let file of files) {
-			if (file.size > maxFileSize) {
-				return tooLarge();
-			}
-		}
-
-		cb(Array.from(files));
-	};
-
-	input.click();
+    const input = document.createElement("input");
+    input.type = "file";
+    input.multiple = multiple ?? false;
+
+    input.onchange = async (e) => {
+        const files = (e.currentTarget as HTMLInputElement)?.files;
+        if (!files) return;
+        for (let file of files) {
+            if (file.size > maxFileSize) {
+                return tooLarge();
+            }
+        }
+
+        cb(Array.from(files));
+    };
+
+    input.click();
 }
 
 export function FileUploader(props: Props) {
-	const { fileType, maxFileSize, remove } = props;
-	const { openScreen } = useIntermediate();
-	const client = useContext(AppContext);
-
-	const [uploading, setUploading] = useState(false);
-
-	function onClick() {
-		if (uploading) return;
-
-		grabFiles(
-			maxFileSize,
-			async (files) => {
-				setUploading(true);
-
-				try {
-					if (props.behaviour === "multi") {
-						props.onChange(files);
-					} else if (props.behaviour === "ask") {
-						props.onChange(files[0]);
-					} else {
-						await props.onUpload(
-							await uploadFile(
-								client.configuration!.features.autumn.url,
-								fileType,
-								files[0],
-							),
-						);
-					}
-				} catch (err) {
-					return openScreen({ id: "error", error: takeError(err) });
-				} finally {
-					setUploading(false);
-				}
-			},
-			() => openScreen({ id: "error", error: "FileTooLarge" }),
-			props.behaviour === "multi",
-		);
-	}
-
-	function removeOrUpload() {
-		if (uploading) return;
-
-		if (props.style === "attachment") {
-			if (props.attached) {
-				props.remove();
-			} else {
-				onClick();
-			}
-		} else {
-			if (props.previewURL) {
-				props.remove();
-			} else {
-				onClick();
-			}
-		}
-	}
-
-	if (props.behaviour === "multi" && props.append) {
-		useEffect(() => {
-			// File pasting.
-			function paste(e: ClipboardEvent) {
-				const items = e.clipboardData?.items;
-				if (typeof items === "undefined") return;
-				if (props.behaviour !== "multi" || !props.append) return;
-
-				let files = [];
-				for (const item of items) {
-					if (!item.type.startsWith("text/")) {
-						const blob = item.getAsFile();
-						if (blob) {
-							if (blob.size > props.maxFileSize) {
-								openScreen({
-									id: "error",
-									error: "FileTooLarge",
-								});
-							}
-
-							files.push(blob);
-						}
-					}
-				}
-
-				props.append(files);
-			}
-
-			// Let the browser know we can drop files.
-			function dragover(e: DragEvent) {
-				e.stopPropagation();
-				e.preventDefault();
-				if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
-			}
-
-			// File dropping.
-			function drop(e: DragEvent) {
-				e.preventDefault();
-				if (props.behaviour !== "multi" || !props.append) return;
-
-				const dropped = e.dataTransfer?.files;
-				if (dropped) {
-					let files = [];
-					for (const item of dropped) {
-						if (item.size > props.maxFileSize) {
-							openScreen({ id: "error", error: "FileTooLarge" });
-						}
-
-						files.push(item);
-					}
-
-					props.append(files);
-				}
-			}
-
-			document.addEventListener("paste", paste);
-			document.addEventListener("dragover", dragover);
-			document.addEventListener("drop", drop);
-
-			return () => {
-				document.removeEventListener("paste", paste);
-				document.removeEventListener("dragover", dragover);
-				document.removeEventListener("drop", drop);
-			};
-		}, [props.append]);
-	}
-
-	if (props.style === "icon" || props.style === "banner") {
-		const { style, previewURL, defaultPreview, width, height } = props;
-		return (
-			<div
-				className={classNames(styles.uploader, {
-					[styles.icon]: style === "icon",
-					[styles.banner]: style === "banner",
-				})}
-				data-uploading={uploading}>
-				<div
-					className={styles.image}
-					style={{
-						backgroundImage:
-							style === "icon"
-								? `url('${previewURL ?? defaultPreview}')`
-								: previewURL
-								? `linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url('${previewURL}')`
-								: "black",
-						width,
-						height,
-					}}
-					onClick={onClick}>
-					{uploading ? (
-						<div className={styles.uploading}>
-							<Preloader type="ring" />
-						</div>
-					) : (
-						<div className={styles.edit}>
-							<Pencil size={30} />
-						</div>
-					)}
-				</div>
-				<div className={styles.modify}>
-					<span onClick={removeOrUpload}>
-						{uploading ? (
-							<Text id="app.main.channel.uploading_file" />
-						) : props.previewURL ? (
-							<Text id="app.settings.actions.remove" />
-						) : (
-							<Text id="app.settings.actions.upload" />
-						)}
-					</span>
-					<span className={styles.small}>
-						<Text
-							id="app.settings.actions.max_filesize"
-							fields={{
-								filesize: determineFileSize(maxFileSize),
-							}}
-						/>
-					</span>
-				</div>
-			</div>
-		);
-	} else if (props.style === "attachment") {
-		const { attached, uploading, cancel, size } = props;
-		return (
-			<IconButton
-				onClick={() => {
-					if (uploading) return cancel();
-					if (attached) return remove();
-					onClick();
-				}}>
-				{uploading ? (
-					<XCircle size={size} />
-				) : attached ? (
-					<X size={size} />
-				) : (
-					<Plus size={size} />
-				)}
-			</IconButton>
-		);
-	}
-
-	return null;
+    const { fileType, maxFileSize, remove } = props;
+    const { openScreen } = useIntermediate();
+    const client = useContext(AppContext);
+
+    const [uploading, setUploading] = useState(false);
+
+    function onClick() {
+        if (uploading) return;
+
+        grabFiles(
+            maxFileSize,
+            async (files) => {
+                setUploading(true);
+
+                try {
+                    if (props.behaviour === "multi") {
+                        props.onChange(files);
+                    } else if (props.behaviour === "ask") {
+                        props.onChange(files[0]);
+                    } else {
+                        await props.onUpload(
+                            await uploadFile(
+                                client.configuration!.features.autumn.url,
+                                fileType,
+                                files[0],
+                            ),
+                        );
+                    }
+                } catch (err) {
+                    return openScreen({ id: "error", error: takeError(err) });
+                } finally {
+                    setUploading(false);
+                }
+            },
+            () => openScreen({ id: "error", error: "FileTooLarge" }),
+            props.behaviour === "multi",
+        );
+    }
+
+    function removeOrUpload() {
+        if (uploading) return;
+
+        if (props.style === "attachment") {
+            if (props.attached) {
+                props.remove();
+            } else {
+                onClick();
+            }
+        } else {
+            if (props.previewURL) {
+                props.remove();
+            } else {
+                onClick();
+            }
+        }
+    }
+
+    if (props.behaviour === "multi" && props.append) {
+        useEffect(() => {
+            // File pasting.
+            function paste(e: ClipboardEvent) {
+                const items = e.clipboardData?.items;
+                if (typeof items === "undefined") return;
+                if (props.behaviour !== "multi" || !props.append) return;
+
+                let files = [];
+                for (const item of items) {
+                    if (!item.type.startsWith("text/")) {
+                        const blob = item.getAsFile();
+                        if (blob) {
+                            if (blob.size > props.maxFileSize) {
+                                openScreen({
+                                    id: "error",
+                                    error: "FileTooLarge",
+                                });
+                            }
+
+                            files.push(blob);
+                        }
+                    }
+                }
+
+                props.append(files);
+            }
+
+            // Let the browser know we can drop files.
+            function dragover(e: DragEvent) {
+                e.stopPropagation();
+                e.preventDefault();
+                if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
+            }
+
+            // File dropping.
+            function drop(e: DragEvent) {
+                e.preventDefault();
+                if (props.behaviour !== "multi" || !props.append) return;
+
+                const dropped = e.dataTransfer?.files;
+                if (dropped) {
+                    let files = [];
+                    for (const item of dropped) {
+                        if (item.size > props.maxFileSize) {
+                            openScreen({ id: "error", error: "FileTooLarge" });
+                        }
+
+                        files.push(item);
+                    }
+
+                    props.append(files);
+                }
+            }
+
+            document.addEventListener("paste", paste);
+            document.addEventListener("dragover", dragover);
+            document.addEventListener("drop", drop);
+
+            return () => {
+                document.removeEventListener("paste", paste);
+                document.removeEventListener("dragover", dragover);
+                document.removeEventListener("drop", drop);
+            };
+        }, [props.append]);
+    }
+
+    if (props.style === "icon" || props.style === "banner") {
+        const { style, previewURL, defaultPreview, width, height } = props;
+        return (
+            <div
+                className={classNames(styles.uploader, {
+                    [styles.icon]: style === "icon",
+                    [styles.banner]: style === "banner",
+                })}
+                data-uploading={uploading}>
+                <div
+                    className={styles.image}
+                    style={{
+                        backgroundImage:
+                            style === "icon"
+                                ? `url('${previewURL ?? defaultPreview}')`
+                                : previewURL
+                                ? `linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url('${previewURL}')`
+                                : "black",
+                        width,
+                        height,
+                    }}
+                    onClick={onClick}>
+                    {uploading ? (
+                        <div className={styles.uploading}>
+                            <Preloader type="ring" />
+                        </div>
+                    ) : (
+                        <div className={styles.edit}>
+                            <Pencil size={30} />
+                        </div>
+                    )}
+                </div>
+                <div className={styles.modify}>
+                    <span onClick={removeOrUpload}>
+                        {uploading ? (
+                            <Text id="app.main.channel.uploading_file" />
+                        ) : props.previewURL ? (
+                            <Text id="app.settings.actions.remove" />
+                        ) : (
+                            <Text id="app.settings.actions.upload" />
+                        )}
+                    </span>
+                    <span className={styles.small}>
+                        <Text
+                            id="app.settings.actions.max_filesize"
+                            fields={{
+                                filesize: determineFileSize(maxFileSize),
+                            }}
+                        />
+                    </span>
+                </div>
+            </div>
+        );
+    } else if (props.style === "attachment") {
+        const { attached, uploading, cancel, size } = props;
+        return (
+            <IconButton
+                onClick={() => {
+                    if (uploading) return cancel();
+                    if (attached) return remove();
+                    onClick();
+                }}>
+                {uploading ? (
+                    <XCircle size={size} />
+                ) : attached ? (
+                    <X size={size} />
+                ) : (
+                    <Plus size={size} />
+                )}
+            </IconButton>
+        );
+    }
+
+    return null;
 }
diff --git a/src/context/revoltjs/Notifications.tsx b/src/context/revoltjs/Notifications.tsx
index 66e0305..40dddc5 100644
--- a/src/context/revoltjs/Notifications.tsx
+++ b/src/context/revoltjs/Notifications.tsx
@@ -9,9 +9,9 @@ import { useTranslation } from "../../lib/i18n";
 
 import { connectState } from "../../redux/connector";
 import {
-	getNotificationState,
-	Notifications,
-	shouldNotify,
+    getNotificationState,
+    Notifications,
+    shouldNotify,
 } from "../../redux/reducers/notifications";
 import { NotificationOptions } from "../../redux/reducers/settings";
 
@@ -19,268 +19,268 @@ import { SoundContext } from "../Settings";
 import { AppContext } from "./RevoltClient";
 
 interface Props {
-	options?: NotificationOptions;
-	notifs: Notifications;
+    options?: NotificationOptions;
+    notifs: Notifications;
 }
 
 const notifications: { [key: string]: Notification } = {};
 
 async function createNotification(
-	title: string,
-	options: globalThis.NotificationOptions,
+    title: string,
+    options: globalThis.NotificationOptions,
 ) {
-	try {
-		return new Notification(title, options);
-	} catch (err) {
-		let sw = await navigator.serviceWorker.getRegistration();
-		sw?.showNotification(title, options);
-	}
+    try {
+        return new Notification(title, options);
+    } catch (err) {
+        let sw = await navigator.serviceWorker.getRegistration();
+        sw?.showNotification(title, options);
+    }
 }
 
 function Notifier({ options, notifs }: Props) {
-	const translate = useTranslation();
-	const showNotification = options?.desktopEnabled ?? false;
+    const translate = useTranslation();
+    const showNotification = options?.desktopEnabled ?? false;
 
-	const client = useContext(AppContext);
-	const { guild: guild_id, channel: channel_id } = useParams<{
-		guild: string;
-		channel: string;
-	}>();
-	const history = useHistory();
-	const playSound = useContext(SoundContext);
+    const client = useContext(AppContext);
+    const { guild: guild_id, channel: channel_id } = useParams<{
+        guild: string;
+        channel: string;
+    }>();
+    const history = useHistory();
+    const playSound = useContext(SoundContext);
 
-	async function message(msg: Message) {
-		if (msg.author === client.user!._id) return;
-		if (msg.channel === channel_id && document.hasFocus()) return;
-		if (client.user!.status?.presence === Users.Presence.Busy) return;
+    async function message(msg: Message) {
+        if (msg.author === client.user!._id) return;
+        if (msg.channel === channel_id && document.hasFocus()) return;
+        if (client.user!.status?.presence === Users.Presence.Busy) return;
 
-		const channel = client.channels.get(msg.channel);
-		const author = client.users.get(msg.author);
-		if (!channel) return;
-		if (author?.relationship === Users.Relationship.Blocked) return;
+        const channel = client.channels.get(msg.channel);
+        const author = client.users.get(msg.author);
+        if (!channel) return;
+        if (author?.relationship === Users.Relationship.Blocked) return;
 
-		const notifState = getNotificationState(notifs, channel);
-		if (!shouldNotify(notifState, msg, client.user!._id)) return;
+        const notifState = getNotificationState(notifs, channel);
+        if (!shouldNotify(notifState, msg, client.user!._id)) return;
 
-		playSound("message");
-		if (!showNotification) return;
+        playSound("message");
+        if (!showNotification) return;
 
-		let title;
-		switch (channel.channel_type) {
-			case "SavedMessages":
-				return;
-			case "DirectMessage":
-				title = `@${author?.username}`;
-				break;
-			case "Group":
-				if (author?._id === SYSTEM_USER_ID) {
-					title = channel.name;
-				} else {
-					title = `@${author?.username} - ${channel.name}`;
-				}
-				break;
-			case "TextChannel":
-				const server = client.servers.get(channel.server);
-				title = `@${author?.username} (#${channel.name}, ${server?.name})`;
-				break;
-			default:
-				title = msg.channel;
-				break;
-		}
+        let title;
+        switch (channel.channel_type) {
+            case "SavedMessages":
+                return;
+            case "DirectMessage":
+                title = `@${author?.username}`;
+                break;
+            case "Group":
+                if (author?._id === SYSTEM_USER_ID) {
+                    title = channel.name;
+                } else {
+                    title = `@${author?.username} - ${channel.name}`;
+                }
+                break;
+            case "TextChannel":
+                const server = client.servers.get(channel.server);
+                title = `@${author?.username} (#${channel.name}, ${server?.name})`;
+                break;
+            default:
+                title = msg.channel;
+                break;
+        }
 
-		let image;
-		if (msg.attachments) {
-			let imageAttachment = msg.attachments.find(
-				(x) => x.metadata.type === "Image",
-			);
-			if (imageAttachment) {
-				image = client.generateFileURL(imageAttachment, {
-					max_side: 720,
-				});
-			}
-		}
+        let image;
+        if (msg.attachments) {
+            let imageAttachment = msg.attachments.find(
+                (x) => x.metadata.type === "Image",
+            );
+            if (imageAttachment) {
+                image = client.generateFileURL(imageAttachment, {
+                    max_side: 720,
+                });
+            }
+        }
 
-		let body, icon;
-		if (typeof msg.content === "string") {
-			body = client.markdownToText(msg.content);
-			icon = client.users.getAvatarURL(msg.author, { max_side: 256 });
-		} else {
-			let users = client.users;
-			switch (msg.content.type) {
-				case "user_added":
-				case "user_remove":
-					body = translate(
-						`app.main.channel.system.${
-							msg.content.type === "user_added"
-								? "added_by"
-								: "removed_by"
-						}`,
-						{
-							user: users.get(msg.content.id)?.username,
-							other_user: users.get(msg.content.by)?.username,
-						},
-					);
-					icon = client.users.getAvatarURL(msg.content.id, {
-						max_side: 256,
-					});
-					break;
-				case "user_joined":
-				case "user_left":
-				case "user_kicked":
-				case "user_banned":
-					body = translate(
-						`app.main.channel.system.${msg.content.type}`,
-						{ user: users.get(msg.content.id)?.username },
-					);
-					icon = client.users.getAvatarURL(msg.content.id, {
-						max_side: 256,
-					});
-					break;
-				case "channel_renamed":
-					body = translate(
-						`app.main.channel.system.channel_renamed`,
-						{
-							user: users.get(msg.content.by)?.username,
-							name: msg.content.name,
-						},
-					);
-					icon = client.users.getAvatarURL(msg.content.by, {
-						max_side: 256,
-					});
-					break;
-				case "channel_description_changed":
-				case "channel_icon_changed":
-					body = translate(
-						`app.main.channel.system.${msg.content.type}`,
-						{ user: users.get(msg.content.by)?.username },
-					);
-					icon = client.users.getAvatarURL(msg.content.by, {
-						max_side: 256,
-					});
-					break;
-			}
-		}
+        let body, icon;
+        if (typeof msg.content === "string") {
+            body = client.markdownToText(msg.content);
+            icon = client.users.getAvatarURL(msg.author, { max_side: 256 });
+        } else {
+            let users = client.users;
+            switch (msg.content.type) {
+                case "user_added":
+                case "user_remove":
+                    body = translate(
+                        `app.main.channel.system.${
+                            msg.content.type === "user_added"
+                                ? "added_by"
+                                : "removed_by"
+                        }`,
+                        {
+                            user: users.get(msg.content.id)?.username,
+                            other_user: users.get(msg.content.by)?.username,
+                        },
+                    );
+                    icon = client.users.getAvatarURL(msg.content.id, {
+                        max_side: 256,
+                    });
+                    break;
+                case "user_joined":
+                case "user_left":
+                case "user_kicked":
+                case "user_banned":
+                    body = translate(
+                        `app.main.channel.system.${msg.content.type}`,
+                        { user: users.get(msg.content.id)?.username },
+                    );
+                    icon = client.users.getAvatarURL(msg.content.id, {
+                        max_side: 256,
+                    });
+                    break;
+                case "channel_renamed":
+                    body = translate(
+                        `app.main.channel.system.channel_renamed`,
+                        {
+                            user: users.get(msg.content.by)?.username,
+                            name: msg.content.name,
+                        },
+                    );
+                    icon = client.users.getAvatarURL(msg.content.by, {
+                        max_side: 256,
+                    });
+                    break;
+                case "channel_description_changed":
+                case "channel_icon_changed":
+                    body = translate(
+                        `app.main.channel.system.${msg.content.type}`,
+                        { user: users.get(msg.content.by)?.username },
+                    );
+                    icon = client.users.getAvatarURL(msg.content.by, {
+                        max_side: 256,
+                    });
+                    break;
+            }
+        }
 
-		let notif = await createNotification(title, {
-			icon,
-			image,
-			body,
-			timestamp: decodeTime(msg._id),
-			tag: msg.channel,
-			badge: "/assets/icons/android-chrome-512x512.png",
-			silent: true,
-		});
+        let notif = await createNotification(title, {
+            icon,
+            image,
+            body,
+            timestamp: decodeTime(msg._id),
+            tag: msg.channel,
+            badge: "/assets/icons/android-chrome-512x512.png",
+            silent: true,
+        });
 
-		if (notif) {
-			notif.addEventListener("click", () => {
-				window.focus();
-				const id = msg.channel;
-				if (id !== channel_id) {
-					let channel = client.channels.get(id);
-					if (channel) {
-						if (channel.channel_type === "TextChannel") {
-							history.push(
-								`/server/${channel.server}/channel/${id}`,
-							);
-						} else {
-							history.push(`/channel/${id}`);
-						}
-					}
-				}
-			});
+        if (notif) {
+            notif.addEventListener("click", () => {
+                window.focus();
+                const id = msg.channel;
+                if (id !== channel_id) {
+                    let channel = client.channels.get(id);
+                    if (channel) {
+                        if (channel.channel_type === "TextChannel") {
+                            history.push(
+                                `/server/${channel.server}/channel/${id}`,
+                            );
+                        } else {
+                            history.push(`/channel/${id}`);
+                        }
+                    }
+                }
+            });
 
-			notifications[msg.channel] = notif;
-			notif.addEventListener(
-				"close",
-				() => delete notifications[msg.channel],
-			);
-		}
-	}
+            notifications[msg.channel] = notif;
+            notif.addEventListener(
+                "close",
+                () => delete notifications[msg.channel],
+            );
+        }
+    }
 
-	async function relationship(user: User, property: string) {
-		if (client.user?.status?.presence === Users.Presence.Busy) return;
-		if (property !== "relationship") return;
-		if (!showNotification) return;
+    async function relationship(user: User, property: string) {
+        if (client.user?.status?.presence === Users.Presence.Busy) return;
+        if (property !== "relationship") return;
+        if (!showNotification) return;
 
-		let event;
-		switch (user.relationship) {
-			case Users.Relationship.Incoming:
-				event = translate("notifications.sent_request", {
-					person: user.username,
-				});
-				break;
-			case Users.Relationship.Friend:
-				event = translate("notifications.now_friends", {
-					person: user.username,
-				});
-				break;
-			default:
-				return;
-		}
+        let event;
+        switch (user.relationship) {
+            case Users.Relationship.Incoming:
+                event = translate("notifications.sent_request", {
+                    person: user.username,
+                });
+                break;
+            case Users.Relationship.Friend:
+                event = translate("notifications.now_friends", {
+                    person: user.username,
+                });
+                break;
+            default:
+                return;
+        }
 
-		let notif = await createNotification(event, {
-			icon: client.users.getAvatarURL(user._id, { max_side: 256 }),
-			badge: "/assets/icons/android-chrome-512x512.png",
-			timestamp: +new Date(),
-		});
+        let notif = await createNotification(event, {
+            icon: client.users.getAvatarURL(user._id, { max_side: 256 }),
+            badge: "/assets/icons/android-chrome-512x512.png",
+            timestamp: +new Date(),
+        });
 
-		notif?.addEventListener("click", () => {
-			history.push(`/friends`);
-		});
-	}
+        notif?.addEventListener("click", () => {
+            history.push(`/friends`);
+        });
+    }
 
-	useEffect(() => {
-		client.addListener("message", message);
-		client.users.addListener("mutation", relationship);
+    useEffect(() => {
+        client.addListener("message", message);
+        client.users.addListener("mutation", relationship);
 
-		return () => {
-			client.removeListener("message", message);
-			client.users.removeListener("mutation", relationship);
-		};
-	}, [client, playSound, guild_id, channel_id, showNotification, notifs]);
+        return () => {
+            client.removeListener("message", message);
+            client.users.removeListener("mutation", relationship);
+        };
+    }, [client, playSound, guild_id, channel_id, showNotification, notifs]);
 
-	useEffect(() => {
-		function visChange() {
-			if (document.visibilityState === "visible") {
-				if (notifications[channel_id]) {
-					notifications[channel_id].close();
-				}
-			}
-		}
+    useEffect(() => {
+        function visChange() {
+            if (document.visibilityState === "visible") {
+                if (notifications[channel_id]) {
+                    notifications[channel_id].close();
+                }
+            }
+        }
 
-		visChange();
+        visChange();
 
-		document.addEventListener("visibilitychange", visChange);
-		return () =>
-			document.removeEventListener("visibilitychange", visChange);
-	}, [guild_id, channel_id]);
+        document.addEventListener("visibilitychange", visChange);
+        return () =>
+            document.removeEventListener("visibilitychange", visChange);
+    }, [guild_id, channel_id]);
 
-	return null;
+    return null;
 }
 
 const NotifierComponent = connectState(
-	Notifier,
-	(state) => {
-		return {
-			options: state.settings.notification,
-			notifs: state.notifications,
-		};
-	},
-	true,
+    Notifier,
+    (state) => {
+        return {
+            options: state.settings.notification,
+            notifs: state.notifications,
+        };
+    },
+    true,
 );
 
 export default function NotificationsComponent() {
-	return (
-		<Switch>
-			<Route path="/server/:server/channel/:channel">
-				<NotifierComponent />
-			</Route>
-			<Route path="/channel/:channel">
-				<NotifierComponent />
-			</Route>
-			<Route path="/">
-				<NotifierComponent />
-			</Route>
-		</Switch>
-	);
+    return (
+        <Switch>
+            <Route path="/server/:server/channel/:channel">
+                <NotifierComponent />
+            </Route>
+            <Route path="/channel/:channel">
+                <NotifierComponent />
+            </Route>
+            <Route path="/">
+                <NotifierComponent />
+            </Route>
+        </Switch>
+    );
 }
diff --git a/src/context/revoltjs/RequiresOnline.tsx b/src/context/revoltjs/RequiresOnline.tsx
index a68ce45..48e347c 100644
--- a/src/context/revoltjs/RequiresOnline.tsx
+++ b/src/context/revoltjs/RequiresOnline.tsx
@@ -10,38 +10,38 @@ import { Children } from "../../types/Preact";
 import { ClientStatus, StatusContext } from "./RevoltClient";
 
 interface Props {
-	children: Children;
+    children: Children;
 }
 
 const Base = styled.div`
-	gap: 16px;
-	padding: 1em;
-	display: flex;
-	user-select: none;
-	align-items: center;
-	flex-direction: row;
-	justify-content: center;
-	color: var(--tertiary-foreground);
-	background: var(--secondary-header);
-
-	> div {
-		font-size: 18px;
-	}
+    gap: 16px;
+    padding: 1em;
+    display: flex;
+    user-select: none;
+    align-items: center;
+    flex-direction: row;
+    justify-content: center;
+    color: var(--tertiary-foreground);
+    background: var(--secondary-header);
+
+    > div {
+        font-size: 18px;
+    }
 `;
 
 export default function RequiresOnline(props: Props) {
-	const status = useContext(StatusContext);
-
-	if (status === ClientStatus.CONNECTING) return <Preloader type="ring" />;
-	if (status !== ClientStatus.ONLINE && status !== ClientStatus.READY)
-		return (
-			<Base>
-				<WifiOff size={16} />
-				<div>
-					<Text id="app.special.requires_online" />
-				</div>
-			</Base>
-		);
-
-	return <>{props.children}</>;
+    const status = useContext(StatusContext);
+
+    if (status === ClientStatus.CONNECTING) return <Preloader type="ring" />;
+    if (status !== ClientStatus.ONLINE && status !== ClientStatus.READY)
+        return (
+            <Base>
+                <WifiOff size={16} />
+                <div>
+                    <Text id="app.special.requires_online" />
+                </div>
+            </Base>
+        );
+
+    return <>{props.children}</>;
 }
diff --git a/src/context/revoltjs/RevoltClient.tsx b/src/context/revoltjs/RevoltClient.tsx
index ddf67a1..6317e1c 100644
--- a/src/context/revoltjs/RevoltClient.tsx
+++ b/src/context/revoltjs/RevoltClient.tsx
@@ -20,23 +20,23 @@ import { registerEvents, setReconnectDisallowed } from "./events";
 import { takeError } from "./util";
 
 export enum ClientStatus {
-	INIT,
-	LOADING,
-	READY,
-	OFFLINE,
-	DISCONNECTED,
-	CONNECTING,
-	RECONNECTING,
-	ONLINE,
+    INIT,
+    LOADING,
+    READY,
+    OFFLINE,
+    DISCONNECTED,
+    CONNECTING,
+    RECONNECTING,
+    ONLINE,
 }
 
 export interface ClientOperations {
-	login: (data: Route<"POST", "/auth/login">["data"]) => Promise<void>;
-	logout: (shouldRequest?: boolean) => Promise<void>;
-	loggedIn: () => boolean;
-	ready: () => boolean;
+    login: (data: Route<"POST", "/auth/login">["data"]) => Promise<void>;
+    logout: (shouldRequest?: boolean) => Promise<void>;
+    loggedIn: () => boolean;
+    ready: () => boolean;
 
-	openDM: (user_id: string) => Promise<string>;
+    openDM: (user_id: string) => Promise<string>;
 }
 
 // By the time they are used, they should all be initialized.
@@ -47,195 +47,195 @@ export const StatusContext = createContext<ClientStatus>(null!);
 export const OperationsContext = createContext<ClientOperations>(null!);
 
 type Props = {
-	auth: AuthState;
-	children: Children;
+    auth: AuthState;
+    children: Children;
 };
 
 function Context({ auth, children }: Props) {
-	const history = useHistory();
-	const { openScreen } = useIntermediate();
-	const [status, setStatus] = useState(ClientStatus.INIT);
-	const [client, setClient] = useState<Client>(
-		undefined as unknown as Client,
-	);
-
-	useEffect(() => {
-		(async () => {
-			let db;
-			try {
-				// Match sw.ts#L23
-				db = await openDB("state", 3, {
-					upgrade(db) {
-						for (let store of [
-							"channels",
-							"servers",
-							"users",
-							"members",
-						]) {
-							db.createObjectStore(store, {
-								keyPath: "_id",
-							});
-						}
-					},
-				});
-			} catch (err) {
-				console.error(
-					"Failed to open IndexedDB store, continuing without.",
-				);
-			}
-
-			const client = new Client({
-				autoReconnect: false,
-				apiURL: import.meta.env.VITE_API_URL,
-				debug: import.meta.env.DEV,
-				db,
-			});
-
-			setClient(client);
-			SingletonMessageRenderer.subscribe(client);
-			setStatus(ClientStatus.LOADING);
-		})();
-	}, []);
-
-	if (status === ClientStatus.INIT) return null;
-
-	const operations: ClientOperations = useMemo(() => {
-		return {
-			login: async (data) => {
-				setReconnectDisallowed(true);
-
-				try {
-					const onboarding = await client.login(data);
-					setReconnectDisallowed(false);
-					const login = () =>
-						dispatch({
-							type: "LOGIN",
-							session: client.session!, // This [null assertion] is ok, we should have a session by now. - insert's words
-						});
-
-					if (onboarding) {
-						openScreen({
-							id: "onboarding",
-							callback: (username: string) =>
-								onboarding(username, true).then(login),
-						});
-					} else {
-						login();
-					}
-				} catch (err) {
-					setReconnectDisallowed(false);
-					throw err;
-				}
-			},
-			logout: async (shouldRequest) => {
-				dispatch({ type: "LOGOUT" });
-
-				client.reset();
-				dispatch({ type: "RESET" });
-
-				openScreen({ id: "none" });
-				setStatus(ClientStatus.READY);
-
-				client.websocket.disconnect();
-
-				if (shouldRequest) {
-					try {
-						await client.logout();
-					} catch (err) {
-						console.error(err);
-					}
-				}
-			},
-			loggedIn: () => typeof auth.active !== "undefined",
-			ready: () =>
-				operations.loggedIn() && typeof client.user !== "undefined",
-			openDM: async (user_id: string) => {
-				let channel = await client.users.openDM(user_id);
-				history.push(`/channel/${channel!._id}`);
-				return channel!._id;
-			},
-		};
-	}, [client, auth.active]);
-
-	useEffect(
-		() => registerEvents({ operations }, setStatus, client),
-		[client],
-	);
-
-	useEffect(() => {
-		(async () => {
-			if (client.db) {
-				await client.restore();
-			}
-
-			if (auth.active) {
-				dispatch({ type: "QUEUE_FAIL_ALL" });
-
-				const active = auth.accounts[auth.active];
-				client.user = client.users.get(active.session.user_id);
-				if (!navigator.onLine) {
-					return setStatus(ClientStatus.OFFLINE);
-				}
-
-				if (operations.ready()) setStatus(ClientStatus.CONNECTING);
-
-				if (navigator.onLine) {
-					await client
-						.fetchConfiguration()
-						.catch(() =>
-							console.error("Failed to connect to API server."),
-						);
-				}
-
-				try {
-					await client.fetchConfiguration();
-					const callback = await client.useExistingSession(
-						active.session,
-					);
-
-					if (callback) {
-						openScreen({ id: "onboarding", callback });
-					}
-				} catch (err) {
-					setStatus(ClientStatus.DISCONNECTED);
-					const error = takeError(err);
-					if (error === "Forbidden" || error === "Unauthorized") {
-						operations.logout(true);
-						openScreen({ id: "signed_out" });
-					} else {
-						openScreen({ id: "error", error });
-					}
-				}
-			} else {
-				try {
-					await client.fetchConfiguration();
-				} catch (err) {
-					console.error("Failed to connect to API server.");
-				}
-
-				setStatus(ClientStatus.READY);
-			}
-		})();
-	}, []);
-
-	if (status === ClientStatus.LOADING) {
-		return <Preloader type="spinner" />;
-	}
-
-	return (
-		<AppContext.Provider value={client}>
-			<StatusContext.Provider value={status}>
-				<OperationsContext.Provider value={operations}>
-					{children}
-				</OperationsContext.Provider>
-			</StatusContext.Provider>
-		</AppContext.Provider>
-	);
+    const history = useHistory();
+    const { openScreen } = useIntermediate();
+    const [status, setStatus] = useState(ClientStatus.INIT);
+    const [client, setClient] = useState<Client>(
+        undefined as unknown as Client,
+    );
+
+    useEffect(() => {
+        (async () => {
+            let db;
+            try {
+                // Match sw.ts#L23
+                db = await openDB("state", 3, {
+                    upgrade(db) {
+                        for (let store of [
+                            "channels",
+                            "servers",
+                            "users",
+                            "members",
+                        ]) {
+                            db.createObjectStore(store, {
+                                keyPath: "_id",
+                            });
+                        }
+                    },
+                });
+            } catch (err) {
+                console.error(
+                    "Failed to open IndexedDB store, continuing without.",
+                );
+            }
+
+            const client = new Client({
+                autoReconnect: false,
+                apiURL: import.meta.env.VITE_API_URL,
+                debug: import.meta.env.DEV,
+                db,
+            });
+
+            setClient(client);
+            SingletonMessageRenderer.subscribe(client);
+            setStatus(ClientStatus.LOADING);
+        })();
+    }, []);
+
+    if (status === ClientStatus.INIT) return null;
+
+    const operations: ClientOperations = useMemo(() => {
+        return {
+            login: async (data) => {
+                setReconnectDisallowed(true);
+
+                try {
+                    const onboarding = await client.login(data);
+                    setReconnectDisallowed(false);
+                    const login = () =>
+                        dispatch({
+                            type: "LOGIN",
+                            session: client.session!, // This [null assertion] is ok, we should have a session by now. - insert's words
+                        });
+
+                    if (onboarding) {
+                        openScreen({
+                            id: "onboarding",
+                            callback: (username: string) =>
+                                onboarding(username, true).then(login),
+                        });
+                    } else {
+                        login();
+                    }
+                } catch (err) {
+                    setReconnectDisallowed(false);
+                    throw err;
+                }
+            },
+            logout: async (shouldRequest) => {
+                dispatch({ type: "LOGOUT" });
+
+                client.reset();
+                dispatch({ type: "RESET" });
+
+                openScreen({ id: "none" });
+                setStatus(ClientStatus.READY);
+
+                client.websocket.disconnect();
+
+                if (shouldRequest) {
+                    try {
+                        await client.logout();
+                    } catch (err) {
+                        console.error(err);
+                    }
+                }
+            },
+            loggedIn: () => typeof auth.active !== "undefined",
+            ready: () =>
+                operations.loggedIn() && typeof client.user !== "undefined",
+            openDM: async (user_id: string) => {
+                let channel = await client.users.openDM(user_id);
+                history.push(`/channel/${channel!._id}`);
+                return channel!._id;
+            },
+        };
+    }, [client, auth.active]);
+
+    useEffect(
+        () => registerEvents({ operations }, setStatus, client),
+        [client],
+    );
+
+    useEffect(() => {
+        (async () => {
+            if (client.db) {
+                await client.restore();
+            }
+
+            if (auth.active) {
+                dispatch({ type: "QUEUE_FAIL_ALL" });
+
+                const active = auth.accounts[auth.active];
+                client.user = client.users.get(active.session.user_id);
+                if (!navigator.onLine) {
+                    return setStatus(ClientStatus.OFFLINE);
+                }
+
+                if (operations.ready()) setStatus(ClientStatus.CONNECTING);
+
+                if (navigator.onLine) {
+                    await client
+                        .fetchConfiguration()
+                        .catch(() =>
+                            console.error("Failed to connect to API server."),
+                        );
+                }
+
+                try {
+                    await client.fetchConfiguration();
+                    const callback = await client.useExistingSession(
+                        active.session,
+                    );
+
+                    if (callback) {
+                        openScreen({ id: "onboarding", callback });
+                    }
+                } catch (err) {
+                    setStatus(ClientStatus.DISCONNECTED);
+                    const error = takeError(err);
+                    if (error === "Forbidden" || error === "Unauthorized") {
+                        operations.logout(true);
+                        openScreen({ id: "signed_out" });
+                    } else {
+                        openScreen({ id: "error", error });
+                    }
+                }
+            } else {
+                try {
+                    await client.fetchConfiguration();
+                } catch (err) {
+                    console.error("Failed to connect to API server.");
+                }
+
+                setStatus(ClientStatus.READY);
+            }
+        })();
+    }, []);
+
+    if (status === ClientStatus.LOADING) {
+        return <Preloader type="spinner" />;
+    }
+
+    return (
+        <AppContext.Provider value={client}>
+            <StatusContext.Provider value={status}>
+                <OperationsContext.Provider value={operations}>
+                    {children}
+                </OperationsContext.Provider>
+            </StatusContext.Provider>
+        </AppContext.Provider>
+    );
 }
 
 export default connectState<{ children: Children }>(Context, (state) => {
-	return {
-		auth: state.auth,
-		sync: state.sync,
-	};
+    return {
+        auth: state.auth,
+        sync: state.sync,
+    };
 });
diff --git a/src/context/revoltjs/StateMonitor.tsx b/src/context/revoltjs/StateMonitor.tsx
index f15d353..85903cf 100644
--- a/src/context/revoltjs/StateMonitor.tsx
+++ b/src/context/revoltjs/StateMonitor.tsx
@@ -13,64 +13,64 @@ import { Typing } from "../../redux/reducers/typing";
 import { AppContext } from "./RevoltClient";
 
 type Props = {
-	messages: QueuedMessage[];
-	typing: Typing;
+    messages: QueuedMessage[];
+    typing: Typing;
 };
 
 function StateMonitor(props: Props) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	useEffect(() => {
-		dispatch({
-			type: "QUEUE_DROP_ALL",
-		});
-	}, []);
+    useEffect(() => {
+        dispatch({
+            type: "QUEUE_DROP_ALL",
+        });
+    }, []);
 
-	useEffect(() => {
-		function add(msg: Message) {
-			if (!msg.nonce) return;
-			if (!props.messages.find((x) => x.id === msg.nonce)) return;
+    useEffect(() => {
+        function add(msg: Message) {
+            if (!msg.nonce) return;
+            if (!props.messages.find((x) => x.id === msg.nonce)) return;
 
-			dispatch({
-				type: "QUEUE_REMOVE",
-				nonce: msg.nonce,
-			});
-		}
+            dispatch({
+                type: "QUEUE_REMOVE",
+                nonce: msg.nonce,
+            });
+        }
 
-		client.addListener("message", add);
-		return () => client.removeListener("message", add);
-	}, [props.messages]);
+        client.addListener("message", add);
+        return () => client.removeListener("message", add);
+    }, [props.messages]);
 
-	useEffect(() => {
-		function removeOld() {
-			if (!props.typing) return;
-			for (let channel of Object.keys(props.typing)) {
-				let users = props.typing[channel];
+    useEffect(() => {
+        function removeOld() {
+            if (!props.typing) return;
+            for (let channel of Object.keys(props.typing)) {
+                let users = props.typing[channel];
 
-				for (let user of users) {
-					if (+new Date() > user.started + 5000) {
-						dispatch({
-							type: "TYPING_STOP",
-							channel,
-							user: user.id,
-						});
-					}
-				}
-			}
-		}
+                for (let user of users) {
+                    if (+new Date() > user.started + 5000) {
+                        dispatch({
+                            type: "TYPING_STOP",
+                            channel,
+                            user: user.id,
+                        });
+                    }
+                }
+            }
+        }
 
-		removeOld();
+        removeOld();
 
-		let interval = setInterval(removeOld, 1000);
-		return () => clearInterval(interval);
-	}, [props.typing]);
+        let interval = setInterval(removeOld, 1000);
+        return () => clearInterval(interval);
+    }, [props.typing]);
 
-	return null;
+    return null;
 }
 
 export default connectState(StateMonitor, (state) => {
-	return {
-		messages: [...state.queue],
-		typing: state.typing,
-	};
+    return {
+        messages: [...state.queue],
+        typing: state.typing,
+    };
 });
diff --git a/src/context/revoltjs/SyncManager.tsx b/src/context/revoltjs/SyncManager.tsx
index ee24d52..86422d6 100644
--- a/src/context/revoltjs/SyncManager.tsx
+++ b/src/context/revoltjs/SyncManager.tsx
@@ -12,135 +12,135 @@ import { connectState } from "../../redux/connector";
 import { Notifications } from "../../redux/reducers/notifications";
 import { Settings } from "../../redux/reducers/settings";
 import {
-	DEFAULT_ENABLED_SYNC,
-	SyncData,
-	SyncKeys,
-	SyncOptions,
+    DEFAULT_ENABLED_SYNC,
+    SyncData,
+    SyncKeys,
+    SyncOptions,
 } from "../../redux/reducers/sync";
 
 import { Language } from "../Locale";
 import { AppContext, ClientStatus, StatusContext } from "./RevoltClient";
 
 type Props = {
-	settings: Settings;
-	locale: Language;
-	sync: SyncOptions;
-	notifications: Notifications;
+    settings: Settings;
+    locale: Language;
+    sync: SyncOptions;
+    notifications: Notifications;
 };
 
 var lastValues: { [key in SyncKeys]?: any } = {};
 
 export function mapSync(
-	packet: Sync.UserSettings,
-	revision?: Record<string, number>,
+    packet: Sync.UserSettings,
+    revision?: Record<string, number>,
 ) {
-	let update: { [key in SyncKeys]?: [number, SyncData[key]] } = {};
-	for (let key of Object.keys(packet)) {
-		let [timestamp, obj] = packet[key];
-		if (timestamp < (revision ?? {})[key] ?? 0) {
-			continue;
-		}
-
-		let object;
-		if (obj[0] === "{") {
-			object = JSON.parse(obj);
-		} else {
-			object = obj;
-		}
-
-		lastValues[key as SyncKeys] = object;
-		update[key as SyncKeys] = [timestamp, object];
-	}
-
-	return update;
+    let update: { [key in SyncKeys]?: [number, SyncData[key]] } = {};
+    for (let key of Object.keys(packet)) {
+        let [timestamp, obj] = packet[key];
+        if (timestamp < (revision ?? {})[key] ?? 0) {
+            continue;
+        }
+
+        let object;
+        if (obj[0] === "{") {
+            object = JSON.parse(obj);
+        } else {
+            object = obj;
+        }
+
+        lastValues[key as SyncKeys] = object;
+        update[key as SyncKeys] = [timestamp, object];
+    }
+
+    return update;
 }
 
 function SyncManager(props: Props) {
-	const client = useContext(AppContext);
-	const status = useContext(StatusContext);
-
-	useEffect(() => {
-		if (status === ClientStatus.ONLINE) {
-			client
-				.syncFetchSettings(
-					DEFAULT_ENABLED_SYNC.filter(
-						(x) => !props.sync?.disabled?.includes(x),
-					),
-				)
-				.then((data) => {
-					dispatch({
-						type: "SYNC_UPDATE",
-						update: mapSync(data),
-					});
-				});
-
-			client
-				.syncFetchUnreads()
-				.then((unreads) => dispatch({ type: "UNREADS_SET", unreads }));
-		}
-	}, [status]);
-
-	function syncChange(key: SyncKeys, data: any) {
-		let timestamp = +new Date();
-		dispatch({
-			type: "SYNC_SET_REVISION",
-			key,
-			timestamp,
-		});
-
-		client.syncSetSettings(
-			{
-				[key]: data,
-			},
-			timestamp,
-		);
-	}
-
-	let disabled = props.sync.disabled ?? [];
-	for (let [key, object] of [
-		["appearance", props.settings.appearance],
-		["theme", props.settings.theme],
-		["locale", props.locale],
-		["notifications", props.notifications],
-	] as [SyncKeys, any][]) {
-		useEffect(() => {
-			if (disabled.indexOf(key) === -1) {
-				if (typeof lastValues[key] !== "undefined") {
-					if (!isEqual(lastValues[key], object)) {
-						syncChange(key, object);
-					}
-				}
-			}
-
-			lastValues[key] = object;
-		}, [disabled, object]);
-	}
-
-	useEffect(() => {
-		function onPacket(packet: ClientboundNotification) {
-			if (packet.type === "UserSettingsUpdate") {
-				let update: { [key in SyncKeys]?: [number, SyncData[key]] } =
-					mapSync(packet.update, props.sync.revision);
-
-				dispatch({
-					type: "SYNC_UPDATE",
-					update,
-				});
-			}
-		}
-
-		client.addListener("packet", onPacket);
-		return () => client.removeListener("packet", onPacket);
-	}, [disabled, props.sync]);
-
-	return null;
+    const client = useContext(AppContext);
+    const status = useContext(StatusContext);
+
+    useEffect(() => {
+        if (status === ClientStatus.ONLINE) {
+            client
+                .syncFetchSettings(
+                    DEFAULT_ENABLED_SYNC.filter(
+                        (x) => !props.sync?.disabled?.includes(x),
+                    ),
+                )
+                .then((data) => {
+                    dispatch({
+                        type: "SYNC_UPDATE",
+                        update: mapSync(data),
+                    });
+                });
+
+            client
+                .syncFetchUnreads()
+                .then((unreads) => dispatch({ type: "UNREADS_SET", unreads }));
+        }
+    }, [status]);
+
+    function syncChange(key: SyncKeys, data: any) {
+        let timestamp = +new Date();
+        dispatch({
+            type: "SYNC_SET_REVISION",
+            key,
+            timestamp,
+        });
+
+        client.syncSetSettings(
+            {
+                [key]: data,
+            },
+            timestamp,
+        );
+    }
+
+    let disabled = props.sync.disabled ?? [];
+    for (let [key, object] of [
+        ["appearance", props.settings.appearance],
+        ["theme", props.settings.theme],
+        ["locale", props.locale],
+        ["notifications", props.notifications],
+    ] as [SyncKeys, any][]) {
+        useEffect(() => {
+            if (disabled.indexOf(key) === -1) {
+                if (typeof lastValues[key] !== "undefined") {
+                    if (!isEqual(lastValues[key], object)) {
+                        syncChange(key, object);
+                    }
+                }
+            }
+
+            lastValues[key] = object;
+        }, [disabled, object]);
+    }
+
+    useEffect(() => {
+        function onPacket(packet: ClientboundNotification) {
+            if (packet.type === "UserSettingsUpdate") {
+                let update: { [key in SyncKeys]?: [number, SyncData[key]] } =
+                    mapSync(packet.update, props.sync.revision);
+
+                dispatch({
+                    type: "SYNC_UPDATE",
+                    update,
+                });
+            }
+        }
+
+        client.addListener("packet", onPacket);
+        return () => client.removeListener("packet", onPacket);
+    }, [disabled, props.sync]);
+
+    return null;
 }
 
 export default connectState(SyncManager, (state) => {
-	return {
-		settings: state.settings,
-		locale: state.locale,
-		sync: state.sync,
-		notifications: state.notifications,
-	};
+    return {
+        settings: state.settings,
+        locale: state.locale,
+        sync: state.sync,
+        notifications: state.notifications,
+    };
 });
diff --git a/src/context/revoltjs/events.ts b/src/context/revoltjs/events.ts
index b603f08..30f80f9 100644
--- a/src/context/revoltjs/events.ts
+++ b/src/context/revoltjs/events.ts
@@ -11,145 +11,145 @@ export var preventReconnect = false;
 let preventUntil = 0;
 
 export function setReconnectDisallowed(allowed: boolean) {
-	preventReconnect = allowed;
+    preventReconnect = allowed;
 }
 
 export function registerEvents(
-	{ operations }: { operations: ClientOperations },
-	setStatus: StateUpdater<ClientStatus>,
-	client: Client,
+    { operations }: { operations: ClientOperations },
+    setStatus: StateUpdater<ClientStatus>,
+    client: Client,
 ) {
-	function attemptReconnect() {
-		if (preventReconnect) return;
-		function reconnect() {
-			preventUntil = +new Date() + 2000;
-			client.websocket.connect().catch((err) => console.error(err));
-		}
-
-		if (+new Date() > preventUntil) {
-			setTimeout(reconnect, 2000);
-		} else {
-			reconnect();
-		}
-	}
-
-	let listeners: Record<string, (...args: any[]) => void> = {
-		connecting: () =>
-			operations.ready() && setStatus(ClientStatus.CONNECTING),
-
-		dropped: () => {
-			if (operations.ready()) {
-				setStatus(ClientStatus.DISCONNECTED);
-				attemptReconnect();
-			}
-		},
-
-		packet: (packet: ClientboundNotification) => {
-			switch (packet.type) {
-				case "ChannelStartTyping": {
-					if (packet.user === client.user?._id) return;
-					dispatch({
-						type: "TYPING_START",
-						channel: packet.id,
-						user: packet.user,
-					});
-					break;
-				}
-				case "ChannelStopTyping": {
-					if (packet.user === client.user?._id) return;
-					dispatch({
-						type: "TYPING_STOP",
-						channel: packet.id,
-						user: packet.user,
-					});
-					break;
-				}
-				case "ChannelAck": {
-					dispatch({
-						type: "UNREADS_MARK_READ",
-						channel: packet.id,
-						message: packet.message_id,
-					});
-					break;
-				}
-			}
-		},
-
-		message: (message: Message) => {
-			if (message.mentions?.includes(client.user!._id)) {
-				dispatch({
-					type: "UNREADS_MENTION",
-					channel: message.channel,
-					message: message._id,
-				});
-			}
-		},
-
-		ready: () => setStatus(ClientStatus.ONLINE),
-	};
-
-	if (import.meta.env.DEV) {
-		listeners = new Proxy(listeners, {
-			get:
-				(target, listener, receiver) =>
-				(...args: unknown[]) => {
-					console.debug(`Calling ${listener.toString()} with`, args);
-					Reflect.get(target, listener)(...args);
-				},
-		});
-	}
-
-	// TODO: clean this a bit and properly handle types
-	for (const listener in listeners) {
-		client.addListener(listener, listeners[listener]);
-	}
-
-	function logMutation(target: string, key: string) {
-		console.log("(o) Object mutated", target, "\nChanged:", key);
-	}
-
-	if (import.meta.env.DEV) {
-		client.users.addListener("mutation", logMutation);
-		client.servers.addListener("mutation", logMutation);
-		client.channels.addListener("mutation", logMutation);
-		client.servers.members.addListener("mutation", logMutation);
-	}
-
-	const online = () => {
-		if (operations.ready()) {
-			setStatus(ClientStatus.RECONNECTING);
-			setReconnectDisallowed(false);
-			attemptReconnect();
-		}
-	};
-
-	const offline = () => {
-		if (operations.ready()) {
-			setReconnectDisallowed(true);
-			client.websocket.disconnect();
-			setStatus(ClientStatus.OFFLINE);
-		}
-	};
-
-	window.addEventListener("online", online);
-	window.addEventListener("offline", offline);
-
-	return () => {
-		for (const listener in listeners) {
-			client.removeListener(
-				listener,
-				listeners[listener as keyof typeof listeners],
-			);
-		}
-
-		if (import.meta.env.DEV) {
-			client.users.removeListener("mutation", logMutation);
-			client.servers.removeListener("mutation", logMutation);
-			client.channels.removeListener("mutation", logMutation);
-			client.servers.members.removeListener("mutation", logMutation);
-		}
-
-		window.removeEventListener("online", online);
-		window.removeEventListener("offline", offline);
-	};
+    function attemptReconnect() {
+        if (preventReconnect) return;
+        function reconnect() {
+            preventUntil = +new Date() + 2000;
+            client.websocket.connect().catch((err) => console.error(err));
+        }
+
+        if (+new Date() > preventUntil) {
+            setTimeout(reconnect, 2000);
+        } else {
+            reconnect();
+        }
+    }
+
+    let listeners: Record<string, (...args: any[]) => void> = {
+        connecting: () =>
+            operations.ready() && setStatus(ClientStatus.CONNECTING),
+
+        dropped: () => {
+            if (operations.ready()) {
+                setStatus(ClientStatus.DISCONNECTED);
+                attemptReconnect();
+            }
+        },
+
+        packet: (packet: ClientboundNotification) => {
+            switch (packet.type) {
+                case "ChannelStartTyping": {
+                    if (packet.user === client.user?._id) return;
+                    dispatch({
+                        type: "TYPING_START",
+                        channel: packet.id,
+                        user: packet.user,
+                    });
+                    break;
+                }
+                case "ChannelStopTyping": {
+                    if (packet.user === client.user?._id) return;
+                    dispatch({
+                        type: "TYPING_STOP",
+                        channel: packet.id,
+                        user: packet.user,
+                    });
+                    break;
+                }
+                case "ChannelAck": {
+                    dispatch({
+                        type: "UNREADS_MARK_READ",
+                        channel: packet.id,
+                        message: packet.message_id,
+                    });
+                    break;
+                }
+            }
+        },
+
+        message: (message: Message) => {
+            if (message.mentions?.includes(client.user!._id)) {
+                dispatch({
+                    type: "UNREADS_MENTION",
+                    channel: message.channel,
+                    message: message._id,
+                });
+            }
+        },
+
+        ready: () => setStatus(ClientStatus.ONLINE),
+    };
+
+    if (import.meta.env.DEV) {
+        listeners = new Proxy(listeners, {
+            get:
+                (target, listener, receiver) =>
+                (...args: unknown[]) => {
+                    console.debug(`Calling ${listener.toString()} with`, args);
+                    Reflect.get(target, listener)(...args);
+                },
+        });
+    }
+
+    // TODO: clean this a bit and properly handle types
+    for (const listener in listeners) {
+        client.addListener(listener, listeners[listener]);
+    }
+
+    function logMutation(target: string, key: string) {
+        console.log("(o) Object mutated", target, "\nChanged:", key);
+    }
+
+    if (import.meta.env.DEV) {
+        client.users.addListener("mutation", logMutation);
+        client.servers.addListener("mutation", logMutation);
+        client.channels.addListener("mutation", logMutation);
+        client.servers.members.addListener("mutation", logMutation);
+    }
+
+    const online = () => {
+        if (operations.ready()) {
+            setStatus(ClientStatus.RECONNECTING);
+            setReconnectDisallowed(false);
+            attemptReconnect();
+        }
+    };
+
+    const offline = () => {
+        if (operations.ready()) {
+            setReconnectDisallowed(true);
+            client.websocket.disconnect();
+            setStatus(ClientStatus.OFFLINE);
+        }
+    };
+
+    window.addEventListener("online", online);
+    window.addEventListener("offline", offline);
+
+    return () => {
+        for (const listener in listeners) {
+            client.removeListener(
+                listener,
+                listeners[listener as keyof typeof listeners],
+            );
+        }
+
+        if (import.meta.env.DEV) {
+            client.users.removeListener("mutation", logMutation);
+            client.servers.removeListener("mutation", logMutation);
+            client.channels.removeListener("mutation", logMutation);
+            client.servers.members.removeListener("mutation", logMutation);
+        }
+
+        window.removeEventListener("online", online);
+        window.removeEventListener("offline", offline);
+    };
 }
diff --git a/src/context/revoltjs/hooks.ts b/src/context/revoltjs/hooks.ts
index ffab5c9..2ceb97e 100644
--- a/src/context/revoltjs/hooks.ts
+++ b/src/context/revoltjs/hooks.ts
@@ -7,226 +7,226 @@ import { useCallback, useContext, useEffect, useState } from "preact/hooks";
 import { AppContext } from "./RevoltClient";
 
 export interface HookContext {
-	client: Client;
-	forceUpdate: () => void;
+    client: Client;
+    forceUpdate: () => void;
 }
 
 export function useForceUpdate(context?: HookContext): HookContext {
-	const client = useContext(AppContext);
-	if (context) return context;
-
-	const H = useState(0);
-	var updateState: (_: number) => void;
-	if (Array.isArray(H)) {
-		let [, u] = H;
-		updateState = u;
-	} else {
-		console.warn("Failed to construct using useState.");
-		updateState = () => {};
-	}
-
-	return { client, forceUpdate: () => updateState(Math.random()) };
+    const client = useContext(AppContext);
+    if (context) return context;
+
+    const H = useState(0);
+    var updateState: (_: number) => void;
+    if (Array.isArray(H)) {
+        let [, u] = H;
+        updateState = u;
+    } else {
+        console.warn("Failed to construct using useState.");
+        updateState = () => {};
+    }
+
+    return { client, forceUpdate: () => updateState(Math.random()) };
 }
 
 // TODO: utils.d.ts maybe?
 type PickProperties<T, U> = Pick<
-	T,
-	{
-		[K in keyof T]: T[K] extends U ? K : never;
-	}[keyof T]
+    T,
+    {
+        [K in keyof T]: T[K] extends U ? K : never;
+    }[keyof T]
 >;
 
 // The keys in Client that are an object
 // for some reason undefined keeps appearing despite there being no reason to so it's filtered out
 type ClientCollectionKey = Exclude<
-	keyof PickProperties<Client, Collection<any>>,
-	undefined
+    keyof PickProperties<Client, Collection<any>>,
+    undefined
 >;
 
 function useObject(
-	type: ClientCollectionKey,
-	id?: string | string[],
-	context?: HookContext,
+    type: ClientCollectionKey,
+    id?: string | string[],
+    context?: HookContext,
 ) {
-	const ctx = useForceUpdate(context);
-
-	function update(target: any) {
-		if (
-			typeof id === "string"
-				? target === id
-				: Array.isArray(id)
-				? id.includes(target)
-				: true
-		) {
-			ctx.forceUpdate();
-		}
-	}
-
-	const map = ctx.client[type];
-	useEffect(() => {
-		map.addListener("update", update);
-		return () => map.removeListener("update", update);
-	}, [id]);
-
-	return typeof id === "string"
-		? map.get(id)
-		: Array.isArray(id)
-		? id.map((x) => map.get(x))
-		: map.toArray();
+    const ctx = useForceUpdate(context);
+
+    function update(target: any) {
+        if (
+            typeof id === "string"
+                ? target === id
+                : Array.isArray(id)
+                ? id.includes(target)
+                : true
+        ) {
+            ctx.forceUpdate();
+        }
+    }
+
+    const map = ctx.client[type];
+    useEffect(() => {
+        map.addListener("update", update);
+        return () => map.removeListener("update", update);
+    }, [id]);
+
+    return typeof id === "string"
+        ? map.get(id)
+        : Array.isArray(id)
+        ? id.map((x) => map.get(x))
+        : map.toArray();
 }
 
 export function useUser(id?: string, context?: HookContext) {
-	if (typeof id === "undefined") return;
-	return useObject("users", id, context) as Readonly<Users.User> | undefined;
+    if (typeof id === "undefined") return;
+    return useObject("users", id, context) as Readonly<Users.User> | undefined;
 }
 
 export function useSelf(context?: HookContext) {
-	const ctx = useForceUpdate(context);
-	return useUser(ctx.client.user!._id, ctx);
+    const ctx = useForceUpdate(context);
+    return useUser(ctx.client.user!._id, ctx);
 }
 
 export function useUsers(ids?: string[], context?: HookContext) {
-	return useObject("users", ids, context) as (
-		| Readonly<Users.User>
-		| undefined
-	)[];
+    return useObject("users", ids, context) as (
+        | Readonly<Users.User>
+        | undefined
+    )[];
 }
 
 export function useChannel(id?: string, context?: HookContext) {
-	if (typeof id === "undefined") return;
-	return useObject("channels", id, context) as
-		| Readonly<Channels.Channel>
-		| undefined;
+    if (typeof id === "undefined") return;
+    return useObject("channels", id, context) as
+        | Readonly<Channels.Channel>
+        | undefined;
 }
 
 export function useChannels(ids?: string[], context?: HookContext) {
-	return useObject("channels", ids, context) as (
-		| Readonly<Channels.Channel>
-		| undefined
-	)[];
+    return useObject("channels", ids, context) as (
+        | Readonly<Channels.Channel>
+        | undefined
+    )[];
 }
 
 export function useServer(id?: string, context?: HookContext) {
-	if (typeof id === "undefined") return;
-	return useObject("servers", id, context) as
-		| Readonly<Servers.Server>
-		| undefined;
+    if (typeof id === "undefined") return;
+    return useObject("servers", id, context) as
+        | Readonly<Servers.Server>
+        | undefined;
 }
 
 export function useServers(ids?: string[], context?: HookContext) {
-	return useObject("servers", ids, context) as (
-		| Readonly<Servers.Server>
-		| undefined
-	)[];
+    return useObject("servers", ids, context) as (
+        | Readonly<Servers.Server>
+        | undefined
+    )[];
 }
 
 export function useDMs(context?: HookContext) {
-	const ctx = useForceUpdate(context);
-
-	function mutation(target: string) {
-		let channel = ctx.client.channels.get(target);
-		if (channel) {
-			if (
-				channel.channel_type === "DirectMessage" ||
-				channel.channel_type === "Group"
-			) {
-				ctx.forceUpdate();
-			}
-		}
-	}
-
-	const map = ctx.client.channels;
-	useEffect(() => {
-		map.addListener("update", mutation);
-		return () => map.removeListener("update", mutation);
-	}, []);
-
-	return map
-		.toArray()
-		.filter(
-			(x) =>
-				x.channel_type === "DirectMessage" ||
-				x.channel_type === "Group" ||
-				x.channel_type === "SavedMessages",
-		) as (
-		| Channels.GroupChannel
-		| Channels.DirectMessageChannel
-		| Channels.SavedMessagesChannel
-	)[];
+    const ctx = useForceUpdate(context);
+
+    function mutation(target: string) {
+        let channel = ctx.client.channels.get(target);
+        if (channel) {
+            if (
+                channel.channel_type === "DirectMessage" ||
+                channel.channel_type === "Group"
+            ) {
+                ctx.forceUpdate();
+            }
+        }
+    }
+
+    const map = ctx.client.channels;
+    useEffect(() => {
+        map.addListener("update", mutation);
+        return () => map.removeListener("update", mutation);
+    }, []);
+
+    return map
+        .toArray()
+        .filter(
+            (x) =>
+                x.channel_type === "DirectMessage" ||
+                x.channel_type === "Group" ||
+                x.channel_type === "SavedMessages",
+        ) as (
+        | Channels.GroupChannel
+        | Channels.DirectMessageChannel
+        | Channels.SavedMessagesChannel
+    )[];
 }
 
 export function useUserPermission(id: string, context?: HookContext) {
-	const ctx = useForceUpdate(context);
+    const ctx = useForceUpdate(context);
 
-	const mutation = (target: string) => target === id && ctx.forceUpdate();
-	useEffect(() => {
-		ctx.client.users.addListener("update", mutation);
-		return () => ctx.client.users.removeListener("update", mutation);
-	}, [id]);
+    const mutation = (target: string) => target === id && ctx.forceUpdate();
+    useEffect(() => {
+        ctx.client.users.addListener("update", mutation);
+        return () => ctx.client.users.removeListener("update", mutation);
+    }, [id]);
 
-	let calculator = new PermissionCalculator(ctx.client);
-	return calculator.forUser(id);
+    let calculator = new PermissionCalculator(ctx.client);
+    return calculator.forUser(id);
 }
 
 export function useChannelPermission(id: string, context?: HookContext) {
-	const ctx = useForceUpdate(context);
-
-	const channel = ctx.client.channels.get(id);
-	const server =
-		channel &&
-		(channel.channel_type === "TextChannel" ||
-			channel.channel_type === "VoiceChannel")
-			? channel.server
-			: undefined;
-
-	const mutation = (target: string) => target === id && ctx.forceUpdate();
-	const mutationServer = (target: string) =>
-		target === server && ctx.forceUpdate();
-	const mutationMember = (target: string) =>
-		target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
-
-	useEffect(() => {
-		ctx.client.channels.addListener("update", mutation);
-
-		if (server) {
-			ctx.client.servers.addListener("update", mutationServer);
-			ctx.client.servers.members.addListener("update", mutationMember);
-		}
-
-		return () => {
-			ctx.client.channels.removeListener("update", mutation);
-
-			if (server) {
-				ctx.client.servers.removeListener("update", mutationServer);
-				ctx.client.servers.members.removeListener(
-					"update",
-					mutationMember,
-				);
-			}
-		};
-	}, [id]);
-
-	let calculator = new PermissionCalculator(ctx.client);
-	return calculator.forChannel(id);
+    const ctx = useForceUpdate(context);
+
+    const channel = ctx.client.channels.get(id);
+    const server =
+        channel &&
+        (channel.channel_type === "TextChannel" ||
+            channel.channel_type === "VoiceChannel")
+            ? channel.server
+            : undefined;
+
+    const mutation = (target: string) => target === id && ctx.forceUpdate();
+    const mutationServer = (target: string) =>
+        target === server && ctx.forceUpdate();
+    const mutationMember = (target: string) =>
+        target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
+
+    useEffect(() => {
+        ctx.client.channels.addListener("update", mutation);
+
+        if (server) {
+            ctx.client.servers.addListener("update", mutationServer);
+            ctx.client.servers.members.addListener("update", mutationMember);
+        }
+
+        return () => {
+            ctx.client.channels.removeListener("update", mutation);
+
+            if (server) {
+                ctx.client.servers.removeListener("update", mutationServer);
+                ctx.client.servers.members.removeListener(
+                    "update",
+                    mutationMember,
+                );
+            }
+        };
+    }, [id]);
+
+    let calculator = new PermissionCalculator(ctx.client);
+    return calculator.forChannel(id);
 }
 
 export function useServerPermission(id: string, context?: HookContext) {
-	const ctx = useForceUpdate(context);
+    const ctx = useForceUpdate(context);
 
-	const mutation = (target: string) => target === id && ctx.forceUpdate();
-	const mutationMember = (target: string) =>
-		target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
+    const mutation = (target: string) => target === id && ctx.forceUpdate();
+    const mutationMember = (target: string) =>
+        target.substr(26) === ctx.client.user!._id && ctx.forceUpdate();
 
-	useEffect(() => {
-		ctx.client.servers.addListener("update", mutation);
-		ctx.client.servers.members.addListener("update", mutationMember);
+    useEffect(() => {
+        ctx.client.servers.addListener("update", mutation);
+        ctx.client.servers.members.addListener("update", mutationMember);
 
-		return () => {
-			ctx.client.servers.removeListener("update", mutation);
-			ctx.client.servers.members.removeListener("update", mutationMember);
-		};
-	}, [id]);
+        return () => {
+            ctx.client.servers.removeListener("update", mutation);
+            ctx.client.servers.members.removeListener("update", mutationMember);
+        };
+    }, [id]);
 
-	let calculator = new PermissionCalculator(ctx.client);
-	return calculator.forServer(id);
+    let calculator = new PermissionCalculator(ctx.client);
+    return calculator.forServer(id);
 }
diff --git a/src/context/revoltjs/util.tsx b/src/context/revoltjs/util.tsx
index e575c71..7d2665d 100644
--- a/src/context/revoltjs/util.tsx
+++ b/src/context/revoltjs/util.tsx
@@ -6,52 +6,52 @@ import { Text } from "preact-i18n";
 import { Children } from "../../types/Preact";
 
 export function takeError(error: any): string {
-	const type = error?.response?.data?.type;
-	let id = type;
-	if (!type) {
-		if (error?.response?.status === 403) {
-			return "Unauthorized";
-		} else if (error && !!error.isAxiosError && !error.response) {
-			return "NetworkError";
-		}
-
-		console.error(error);
-		return "UnknownError";
-	}
-
-	return id;
+    const type = error?.response?.data?.type;
+    let id = type;
+    if (!type) {
+        if (error?.response?.status === 403) {
+            return "Unauthorized";
+        } else if (error && !!error.isAxiosError && !error.response) {
+            return "NetworkError";
+        }
+
+        console.error(error);
+        return "UnknownError";
+    }
+
+    return id;
 }
 
 export function getChannelName(
-	client: Client,
-	channel: Channel,
-	prefixType?: boolean,
+    client: Client,
+    channel: Channel,
+    prefixType?: boolean,
 ): Children {
-	if (channel.channel_type === "SavedMessages")
-		return <Text id="app.navigation.tabs.saved" />;
-
-	if (channel.channel_type === "DirectMessage") {
-		let uid = client.channels.getRecipient(channel._id);
-		return (
-			<>
-				{prefixType && "@"}
-				{client.users.get(uid)?.username}
-			</>
-		);
-	}
-
-	if (channel.channel_type === "TextChannel" && prefixType) {
-		return <>#{channel.name}</>;
-	}
-
-	return <>{channel.name}</>;
+    if (channel.channel_type === "SavedMessages")
+        return <Text id="app.navigation.tabs.saved" />;
+
+    if (channel.channel_type === "DirectMessage") {
+        let uid = client.channels.getRecipient(channel._id);
+        return (
+            <>
+                {prefixType && "@"}
+                {client.users.get(uid)?.username}
+            </>
+        );
+    }
+
+    if (channel.channel_type === "TextChannel" && prefixType) {
+        return <>#{channel.name}</>;
+    }
+
+    return <>{channel.name}</>;
 }
 
 export type MessageObject = Omit<Message, "edited"> & { edited?: string };
 export function mapMessage(message: Partial<Message>) {
-	const { edited, ...msg } = message;
-	return {
-		...msg,
-		edited: edited?.$date,
-	} as MessageObject;
+    const { edited, ...msg } = message;
+    return {
+        ...msg,
+        edited: edited?.$date,
+    } as MessageObject;
 }
diff --git a/src/env.d.ts b/src/env.d.ts
index 3b2063f..25be5fe 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -1,4 +1,4 @@
 interface ImportMetaEnv {
-	VITE_API_URL: string;
-	VITE_THEMES_URL: string;
+    VITE_API_URL: string;
+    VITE_THEMES_URL: string;
 }
diff --git a/src/lib/ConditionalLink.tsx b/src/lib/ConditionalLink.tsx
index 53f8955..dd69d1a 100644
--- a/src/lib/ConditionalLink.tsx
+++ b/src/lib/ConditionalLink.tsx
@@ -1,16 +1,16 @@
 import { Link, LinkProps } from "react-router-dom";
 
 type Props = LinkProps &
-	JSX.HTMLAttributes<HTMLAnchorElement> & {
-		active: boolean;
-	};
+    JSX.HTMLAttributes<HTMLAnchorElement> & {
+        active: boolean;
+    };
 
 export default function ConditionalLink(props: Props) {
-	const { active, ...linkProps } = props;
+    const { active, ...linkProps } = props;
 
-	if (active) {
-		return <a>{props.children}</a>;
-	} else {
-		return <Link {...linkProps} />;
-	}
+    if (active) {
+        return <a>{props.children}</a>;
+    } else {
+        return <Link {...linkProps} />;
+    }
 }
diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx
index 8f73e27..c0fac9f 100644
--- a/src/lib/ContextMenus.tsx
+++ b/src/lib/ContextMenus.tsx
@@ -1,35 +1,35 @@
 import {
-	At,
-	Bell,
-	BellOff,
-	Check,
-	CheckSquare,
-	ChevronRight,
-	Block,
-	Square,
-	LeftArrowAlt,
-	Trash,
+    At,
+    Bell,
+    BellOff,
+    Check,
+    CheckSquare,
+    ChevronRight,
+    Block,
+    Square,
+    LeftArrowAlt,
+    Trash,
 } from "@styled-icons/boxicons-regular";
 import { Cog } from "@styled-icons/boxicons-solid";
 import { useHistory } from "react-router-dom";
 import {
-	Attachment,
-	Channels,
-	Message,
-	Servers,
-	Users,
+    Attachment,
+    Channels,
+    Message,
+    Servers,
+    Users,
 } from "revolt.js/dist/api/objects";
 import {
-	ChannelPermission,
-	ServerPermission,
-	UserPermission,
+    ChannelPermission,
+    ServerPermission,
+    UserPermission,
 } from "revolt.js/dist/api/permissions";
 
 import {
-	ContextMenu,
-	ContextMenuWithData,
-	MenuItem,
-	openContextMenu,
+    ContextMenu,
+    ContextMenuWithData,
+    MenuItem,
+    openContextMenu,
 } from "preact-context-menu";
 import { Text } from "preact-i18n";
 import { useContext } from "preact/hooks";
@@ -37,26 +37,26 @@ import { useContext } from "preact/hooks";
 import { dispatch } from "../redux";
 import { connectState } from "../redux/connector";
 import {
-	getNotificationState,
-	Notifications,
-	NotificationState,
+    getNotificationState,
+    Notifications,
+    NotificationState,
 } from "../redux/reducers/notifications";
 import { QueuedMessage } from "../redux/reducers/queue";
 
 import { useIntermediate } from "../context/intermediate/Intermediate";
 import {
-	AppContext,
-	ClientStatus,
-	StatusContext,
+    AppContext,
+    ClientStatus,
+    StatusContext,
 } from "../context/revoltjs/RevoltClient";
 import {
-	useChannel,
-	useChannelPermission,
-	useForceUpdate,
-	useServer,
-	useServerPermission,
-	useUser,
-	useUserPermission,
+    useChannel,
+    useChannelPermission,
+    useForceUpdate,
+    useServer,
+    useServerPermission,
+    useUser,
+    useUserPermission,
 } from "../context/revoltjs/hooks";
 import { takeError } from "../context/revoltjs/util";
 
@@ -68,951 +68,951 @@ import { Children } from "../types/Preact";
 import { internalEmit } from "./eventEmitter";
 
 interface ContextMenuData {
-	user?: string;
-	server?: string;
-	server_list?: string;
-	channel?: string;
-	message?: Message;
-
-	unread?: boolean;
-	queued?: QueuedMessage;
-	contextualChannel?: string;
+    user?: string;
+    server?: string;
+    server_list?: string;
+    channel?: string;
+    message?: Message;
+
+    unread?: boolean;
+    queued?: QueuedMessage;
+    contextualChannel?: string;
 }
 
 type Action =
-	| { action: "copy_id"; id: string }
-	| { action: "copy_selection" }
-	| { action: "copy_text"; content: string }
-	| { action: "mark_as_read"; channel: Channels.Channel }
-	| { action: "retry_message"; message: QueuedMessage }
-	| { action: "cancel_message"; message: QueuedMessage }
-	| { action: "mention"; user: string }
-	| { action: "reply_message"; id: string }
-	| { action: "quote_message"; content: string }
-	| { action: "edit_message"; id: string }
-	| { action: "delete_message"; target: Channels.Message }
-	| { action: "open_file"; attachment: Attachment }
-	| { action: "save_file"; attachment: Attachment }
-	| { action: "copy_file_link"; attachment: Attachment }
-	| { action: "open_link"; link: string }
-	| { action: "copy_link"; link: string }
-	| { action: "remove_member"; channel: string; user: string }
-	| { action: "kick_member"; target: Servers.Server; user: string }
-	| { action: "ban_member"; target: Servers.Server; user: string }
-	| { action: "view_profile"; user: string }
-	| { action: "message_user"; user: string }
-	| { action: "block_user"; user: Users.User }
-	| { action: "unblock_user"; user: Users.User }
-	| { action: "add_friend"; user: Users.User }
-	| { action: "remove_friend"; user: Users.User }
-	| { action: "cancel_friend"; user: Users.User }
-	| { action: "set_presence"; presence: Users.Presence }
-	| { action: "set_status" }
-	| { action: "clear_status" }
-	| { action: "create_channel"; target: Servers.Server }
-	| {
-			action: "create_invite";
-			target:
-				| Channels.GroupChannel
-				| Channels.TextChannel
-				| Channels.VoiceChannel;
-	  }
-	| { action: "leave_group"; target: Channels.GroupChannel }
-	| {
-			action: "delete_channel";
-			target: Channels.TextChannel | Channels.VoiceChannel;
-	  }
-	| { action: "close_dm"; target: Channels.DirectMessageChannel }
-	| { action: "leave_server"; target: Servers.Server }
-	| { action: "delete_server"; target: Servers.Server }
-	| { action: "open_notification_options"; channel: Channels.Channel }
-	| { action: "open_settings" }
-	| { action: "open_channel_settings"; id: string }
-	| { action: "open_server_settings"; id: string }
-	| { action: "open_server_channel_settings"; server: string; id: string }
-	| {
-			action: "set_notification_state";
-			key: string;
-			state?: NotificationState;
-	  };
+    | { action: "copy_id"; id: string }
+    | { action: "copy_selection" }
+    | { action: "copy_text"; content: string }
+    | { action: "mark_as_read"; channel: Channels.Channel }
+    | { action: "retry_message"; message: QueuedMessage }
+    | { action: "cancel_message"; message: QueuedMessage }
+    | { action: "mention"; user: string }
+    | { action: "reply_message"; id: string }
+    | { action: "quote_message"; content: string }
+    | { action: "edit_message"; id: string }
+    | { action: "delete_message"; target: Channels.Message }
+    | { action: "open_file"; attachment: Attachment }
+    | { action: "save_file"; attachment: Attachment }
+    | { action: "copy_file_link"; attachment: Attachment }
+    | { action: "open_link"; link: string }
+    | { action: "copy_link"; link: string }
+    | { action: "remove_member"; channel: string; user: string }
+    | { action: "kick_member"; target: Servers.Server; user: string }
+    | { action: "ban_member"; target: Servers.Server; user: string }
+    | { action: "view_profile"; user: string }
+    | { action: "message_user"; user: string }
+    | { action: "block_user"; user: Users.User }
+    | { action: "unblock_user"; user: Users.User }
+    | { action: "add_friend"; user: Users.User }
+    | { action: "remove_friend"; user: Users.User }
+    | { action: "cancel_friend"; user: Users.User }
+    | { action: "set_presence"; presence: Users.Presence }
+    | { action: "set_status" }
+    | { action: "clear_status" }
+    | { action: "create_channel"; target: Servers.Server }
+    | {
+          action: "create_invite";
+          target:
+              | Channels.GroupChannel
+              | Channels.TextChannel
+              | Channels.VoiceChannel;
+      }
+    | { action: "leave_group"; target: Channels.GroupChannel }
+    | {
+          action: "delete_channel";
+          target: Channels.TextChannel | Channels.VoiceChannel;
+      }
+    | { action: "close_dm"; target: Channels.DirectMessageChannel }
+    | { action: "leave_server"; target: Servers.Server }
+    | { action: "delete_server"; target: Servers.Server }
+    | { action: "open_notification_options"; channel: Channels.Channel }
+    | { action: "open_settings" }
+    | { action: "open_channel_settings"; id: string }
+    | { action: "open_server_settings"; id: string }
+    | { action: "open_server_channel_settings"; server: string; id: string }
+    | {
+          action: "set_notification_state";
+          key: string;
+          state?: NotificationState;
+      };
 
 type Props = {
-	notifications: Notifications;
+    notifications: Notifications;
 };
 
 function ContextMenus(props: Props) {
-	const { openScreen, writeClipboard } = useIntermediate();
-	const client = useContext(AppContext);
-	const userId = client.user!._id;
-	const status = useContext(StatusContext);
-	const isOnline = status === ClientStatus.ONLINE;
-	const history = useHistory();
-
-	function contextClick(data?: Action) {
-		if (typeof data === "undefined") return;
-
-		(async () => {
-			switch (data.action) {
-				case "copy_id":
-					writeClipboard(data.id);
-					break;
-				case "copy_selection":
-					writeClipboard(document.getSelection()?.toString() ?? "");
-					break;
-				case "mark_as_read":
-					{
-						if (
-							data.channel.channel_type === "SavedMessages" ||
-							data.channel.channel_type === "VoiceChannel"
-						)
-							return;
-
-						let message =
-							data.channel.channel_type === "TextChannel"
-								? data.channel.last_message
-								: data.channel.last_message._id;
-						dispatch({
-							type: "UNREADS_MARK_READ",
-							channel: data.channel._id,
-							message,
-						});
-
-						client.req(
-							"PUT",
-							`/channels/${data.channel._id}/ack/${message}` as "/channels/id/ack/id",
-						);
-					}
-					break;
-
-				case "retry_message":
-					{
-						const nonce = data.message.id;
-						const fail = (error: any) =>
-							dispatch({
-								type: "QUEUE_FAIL",
-								nonce,
-								error,
-							});
-
-						client.channels
-							.sendMessage(data.message.channel, {
-								nonce: data.message.id,
-								content: data.message.data.content as string,
-								replies: data.message.data.replies,
-							})
-							.catch(fail);
-
-						dispatch({
-							type: "QUEUE_START",
-							nonce,
-						});
-					}
-					break;
-
-				case "cancel_message":
-					{
-						dispatch({
-							type: "QUEUE_REMOVE",
-							nonce: data.message.id,
-						});
-					}
-					break;
-
-				case "mention":
-					{
-						internalEmit(
-							"MessageBox",
-							"append",
-							`<@${data.user}>`,
-							"mention",
-						);
-					}
-					break;
-
-				case "copy_text":
-					writeClipboard(data.content);
-					break;
-
-				case "reply_message":
-					{
-						internalEmit("ReplyBar", "add", data.id);
-					}
-					break;
-
-				case "quote_message":
-					{
-						internalEmit(
-							"MessageBox",
-							"append",
-							data.content,
-							"quote",
-						);
-					}
-					break;
-
-				case "edit_message":
-					{
-						internalEmit(
-							"MessageRenderer",
-							"edit_message",
-							data.id,
-						);
-					}
-					break;
-
-				case "open_file":
-					{
-						window
-							.open(
-								client.generateFileURL(data.attachment),
-								"_blank",
-							)
-							?.focus();
-					}
-					break;
-
-				case "save_file":
-					{
-						window.open(
-							// ! FIXME: do this from revolt.js
-							client
-								.generateFileURL(data.attachment)
-								?.replace(
-									"attachments",
-									"attachments/download",
-								),
-							"_blank",
-						);
-					}
-					break;
-
-				case "copy_file_link":
-					{
-						const { filename } = data.attachment;
-						writeClipboard(
-							// ! FIXME: do from r.js
-							client.generateFileURL(data.attachment) +
-								`/${encodeURI(filename)}`,
-						);
-					}
-					break;
-
-				case "open_link":
-					{
-						window.open(data.link, "_blank")?.focus();
-					}
-					break;
-
-				case "copy_link":
-					{
-						writeClipboard(data.link);
-					}
-					break;
-
-				case "remove_member":
-					{
-						client.channels.removeMember(data.channel, data.user);
-					}
-					break;
-
-				case "view_profile":
-					openScreen({ id: "profile", user_id: data.user });
-					break;
-
-				case "message_user":
-					{
-						const channel = await client.users.openDM(data.user);
-						if (channel) {
-							history.push(`/channel/${channel._id}`);
-						}
-					}
-					break;
-
-				case "add_friend":
-					{
-						await client.users.addFriend(data.user.username);
-					}
-					break;
-
-				case "block_user":
-					openScreen({
-						id: "special_prompt",
-						type: "block_user",
-						target: data.user,
-					});
-					break;
-				case "unblock_user":
-					await client.users.unblockUser(data.user._id);
-					break;
-				case "remove_friend":
-					openScreen({
-						id: "special_prompt",
-						type: "unfriend_user",
-						target: data.user,
-					});
-					break;
-				case "cancel_friend":
-					await client.users.removeFriend(data.user._id);
-					break;
-
-				case "set_presence":
-					{
-						await client.users.editUser({
-							status: {
-								...client.user?.status,
-								presence: data.presence,
-							},
-						});
-					}
-					break;
-
-				case "set_status":
-					openScreen({
-						id: "special_input",
-						type: "set_custom_status",
-					});
-					break;
-
-				case "clear_status":
-					{
-						let { text, ...status } = client.user?.status ?? {};
-						await client.users.editUser({ status });
-					}
-					break;
-
-				case "leave_group":
-				case "close_dm":
-				case "leave_server":
-				case "delete_channel":
-				case "delete_server":
-				case "delete_message":
-				case "create_channel":
-				case "create_invite":
-					// The any here is because typescript flattens the case types into a single type and type structure and specifity is lost or whatever
-					openScreen({
-						id: "special_prompt",
-						type: data.action,
-						target: data.target as any,
-					});
-					break;
-
-				case "ban_member":
-				case "kick_member":
-					openScreen({
-						id: "special_prompt",
-						type: data.action,
-						target: data.target,
-						user: data.user,
-					});
-					break;
-
-				case "open_notification_options": {
-					openContextMenu("NotificationOptions", {
-						channel: data.channel,
-					});
-					break;
-				}
-
-				case "open_settings":
-					history.push("/settings");
-					break;
-				case "open_channel_settings":
-					history.push(`/channel/${data.id}/settings`);
-					break;
-				case "open_server_channel_settings":
-					history.push(
-						`/server/${data.server}/channel/${data.id}/settings`,
-					);
-					break;
-				case "open_server_settings":
-					history.push(`/server/${data.id}/settings`);
-					break;
-
-				case "set_notification_state": {
-					const { key, state } = data;
-					if (state) {
-						dispatch({ type: "NOTIFICATIONS_SET", key, state });
-					} else {
-						dispatch({ type: "NOTIFICATIONS_REMOVE", key });
-					}
-					break;
-				}
-			}
-		})().catch((err) => {
-			openScreen({ id: "error", error: takeError(err) });
-		});
-	}
-
-	return (
-		<>
-			<ContextMenuWithData id="Menu" onClose={contextClick}>
-				{({
-					user: uid,
-					channel: cid,
-					server: sid,
-					message,
-					server_list,
-					queued,
-					unread,
-					contextualChannel: cxid,
-				}: ContextMenuData) => {
-					const forceUpdate = useForceUpdate();
-					const elements: Children[] = [];
-					var lastDivider = false;
-
-					function generateAction(
-						action: Action,
-						locale?: string,
-						disabled?: boolean,
-						tip?: Children,
-					) {
-						lastDivider = false;
-						elements.push(
-							<MenuItem data={action} disabled={disabled}>
-								<Text
-									id={`app.context_menu.${
-										locale ?? action.action
-									}`}
-								/>
-								{tip && <div className="tip">{tip}</div>}
-							</MenuItem>,
-						);
-					}
-
-					function pushDivider() {
-						if (lastDivider || elements.length === 0) return;
-						lastDivider = true;
-						elements.push(<LineDivider />);
-					}
-
-					if (server_list) {
-						let server = useServer(server_list, forceUpdate);
-						let permissions = useServerPermission(
-							server_list,
-							forceUpdate,
-						);
-						if (server) {
-							if (permissions & ServerPermission.ManageChannels)
-								generateAction({
-									action: "create_channel",
-									target: server,
-								});
-							if (permissions & ServerPermission.ManageServer)
-								generateAction({
-									action: "open_server_settings",
-									id: server_list,
-								});
-						}
-
-						return elements;
-					}
-
-					if (document.getSelection()?.toString().length ?? 0 > 0) {
-						generateAction(
-							{ action: "copy_selection" },
-							undefined,
-							undefined,
-							<Text id="shortcuts.ctrlc" />,
-						);
-						pushDivider();
-					}
-
-					const channel = useChannel(cid, forceUpdate);
-					const contextualChannel = useChannel(cxid, forceUpdate);
-					const targetChannel = channel ?? contextualChannel;
-
-					const user = useUser(uid, forceUpdate);
-					const serverChannel =
-						targetChannel &&
-						(targetChannel.channel_type === "TextChannel" ||
-							targetChannel.channel_type === "VoiceChannel")
-							? targetChannel
-							: undefined;
-					const server = useServer(
-						serverChannel ? serverChannel.server : sid,
-						forceUpdate,
-					);
-
-					const channelPermissions = targetChannel
-						? useChannelPermission(targetChannel._id, forceUpdate)
-						: 0;
-					const serverPermissions = server
-						? useServerPermission(server._id, forceUpdate)
-						: serverChannel
-						? useServerPermission(serverChannel.server, forceUpdate)
-						: 0;
-					const userPermissions = user
-						? useUserPermission(user._id, forceUpdate)
-						: 0;
-
-					if (channel && unread) {
-						generateAction({ action: "mark_as_read", channel });
-					}
-
-					if (contextualChannel) {
-						if (user && user._id !== userId) {
-							generateAction({
-								action: "mention",
-								user: user._id,
-							});
-
-							pushDivider();
-						}
-					}
-
-					if (user) {
-						let actions: Action["action"][];
-						switch (user.relationship) {
-							case Users.Relationship.User:
-								actions = [];
-								break;
-							case Users.Relationship.Friend:
-								actions = ["remove_friend", "block_user"];
-								break;
-							case Users.Relationship.Incoming:
-								actions = [
-									"add_friend",
-									"cancel_friend",
-									"block_user",
-								];
-								break;
-							case Users.Relationship.Outgoing:
-								actions = ["cancel_friend", "block_user"];
-								break;
-							case Users.Relationship.Blocked:
-								actions = ["unblock_user"];
-								break;
-							case Users.Relationship.BlockedOther:
-								actions = ["block_user"];
-								break;
-							case Users.Relationship.None:
-							default:
-								actions = ["add_friend", "block_user"];
-						}
-
-						if (userPermissions & UserPermission.ViewProfile) {
-							generateAction({
-								action: "view_profile",
-								user: user._id,
-							});
-						}
-
-						if (
-							user._id !== userId &&
-							userPermissions & UserPermission.SendMessage
-						) {
-							generateAction({
-								action: "message_user",
-								user: user._id,
-							});
-						}
-
-						for (let i = 0; i < actions.length; i++) {
-							// The any here is because typescript can't determine that user the actions are linked together correctly
-							generateAction({ action: actions[i] as any, user });
-						}
-					}
-
-					if (contextualChannel) {
-						if (contextualChannel.channel_type === "Group" && uid) {
-							if (
-								contextualChannel.owner === userId &&
-								userId !== uid
-							) {
-								generateAction({
-									action: "remove_member",
-									channel: contextualChannel._id,
-									user: uid,
-								});
-							}
-						}
-
-						if (
-							server &&
-							uid &&
-							userId !== uid &&
-							uid !== server.owner
-						) {
-							if (
-								serverPermissions & ServerPermission.KickMembers
-							)
-								generateAction({
-									action: "kick_member",
-									target: server,
-									user: uid,
-								});
-
-							if (serverPermissions & ServerPermission.BanMembers)
-								generateAction({
-									action: "ban_member",
-									target: server,
-									user: uid,
-								});
-						}
-					}
-
-					if (queued) {
-						generateAction({
-							action: "retry_message",
-							message: queued,
-						});
-
-						generateAction({
-							action: "cancel_message",
-							message: queued,
-						});
-					}
-
-					if (message && !queued) {
-						generateAction({
-							action: "reply_message",
-							id: message._id,
-						});
-
-						if (
-							typeof message.content === "string" &&
-							message.content.length > 0
-						) {
-							generateAction({
-								action: "quote_message",
-								content: message.content,
-							});
-
-							generateAction({
-								action: "copy_text",
-								content: message.content,
-							});
-						}
-
-						if (message.author === userId) {
-							generateAction({
-								action: "edit_message",
-								id: message._id,
-							});
-						}
-
-						if (
-							message.author === userId ||
-							channelPermissions &
-								ChannelPermission.ManageMessages
-						) {
-							generateAction({
-								action: "delete_message",
-								target: message,
-							});
-						}
-
-						if (message.attachments) {
-							pushDivider();
-							const { metadata } = message.attachments[0];
-							const { type } = metadata;
-
-							generateAction(
-								{
-									action: "open_file",
-									attachment: message.attachments[0],
-								},
-								type === "Image"
-									? "open_image"
-									: type === "Video"
-									? "open_video"
-									: "open_file",
-							);
-
-							generateAction(
-								{
-									action: "save_file",
-									attachment: message.attachments[0],
-								},
-								type === "Image"
-									? "save_image"
-									: type === "Video"
-									? "save_video"
-									: "save_file",
-							);
-
-							generateAction(
-								{
-									action: "copy_file_link",
-									attachment: message.attachments[0],
-								},
-								"copy_link",
-							);
-						}
-
-						if (document.activeElement?.tagName === "A") {
-							let link =
-								document.activeElement.getAttribute("href");
-							if (link) {
-								pushDivider();
-								generateAction({ action: "open_link", link });
-								generateAction({ action: "copy_link", link });
-							}
-						}
-					}
-
-					let id = sid ?? cid ?? uid ?? message?._id;
-					if (id) {
-						pushDivider();
-
-						if (channel) {
-							if (channel.channel_type !== "VoiceChannel") {
-								generateAction(
-									{
-										action: "open_notification_options",
-										channel,
-									},
-									undefined,
-									undefined,
-									<ChevronRight size={24} />,
-								);
-							}
-
-							switch (channel.channel_type) {
-								case "Group":
-									// ! generateAction({ action: "create_invite", target: channel }); FIXME: add support for group invites
-									generateAction(
-										{
-											action: "open_channel_settings",
-											id: channel._id,
-										},
-										"open_group_settings",
-									);
-									generateAction(
-										{
-											action: "leave_group",
-											target: channel,
-										},
-										"leave_group",
-									);
-									break;
-								case "DirectMessage":
-									generateAction({
-										action: "close_dm",
-										target: channel,
-									});
-									break;
-								case "TextChannel":
-								case "VoiceChannel":
-									// ! FIXME: add permission for invites
-									generateAction({
-										action: "create_invite",
-										target: channel,
-									});
-
-									if (
-										serverPermissions &
-										ServerPermission.ManageServer
-									)
-										generateAction(
-											{
-												action: "open_server_channel_settings",
-												server: channel.server,
-												id: channel._id,
-											},
-											"open_channel_settings",
-										);
-
-									if (
-										serverPermissions &
-										ServerPermission.ManageChannels
-									)
-										generateAction({
-											action: "delete_channel",
-											target: channel,
-										});
-
-									break;
-							}
-						}
-
-						if (sid && server) {
-							if (
-								serverPermissions &
-								ServerPermission.ManageServer
-							)
-								generateAction(
-									{
-										action: "open_server_settings",
-										id: server._id,
-									},
-									"open_server_settings",
-								);
-
-							if (userId === server.owner) {
-								generateAction(
-									{ action: "delete_server", target: server },
-									"delete_server",
-								);
-							} else {
-								generateAction(
-									{ action: "leave_server", target: server },
-									"leave_server",
-								);
-							}
-						}
-
-						generateAction(
-							{ action: "copy_id", id },
-							sid
-								? "copy_sid"
-								: cid
-								? "copy_cid"
-								: message
-								? "copy_mid"
-								: "copy_uid",
-						);
-					}
-
-					return elements;
-				}}
-			</ContextMenuWithData>
-			<ContextMenuWithData
-				id="Status"
-				onClose={contextClick}
-				className="Status">
-				{() => (
-					<>
-						<div className="header">
-							<div className="main">
-								<div>@{client.user!.username}</div>
-								<div className="status">
-									<UserStatus user={client.user!} />
-								</div>
-							</div>
-							<IconButton>
-								<MenuItem data={{ action: "open_settings" }}>
-									<Cog size={18} />
-								</MenuItem>
-							</IconButton>
-						</div>
-						<LineDivider />
-						<MenuItem
-							data={{
-								action: "set_presence",
-								presence: Users.Presence.Online,
-							}}
-							disabled={!isOnline}>
-							<div className="indicator online" />
-							<Text id={`app.status.online`} />
-						</MenuItem>
-						<MenuItem
-							data={{
-								action: "set_presence",
-								presence: Users.Presence.Idle,
-							}}
-							disabled={!isOnline}>
-							<div className="indicator idle" />
-							<Text id={`app.status.idle`} />
-						</MenuItem>
-						<MenuItem
-							data={{
-								action: "set_presence",
-								presence: Users.Presence.Busy,
-							}}
-							disabled={!isOnline}>
-							<div className="indicator busy" />
-							<Text id={`app.status.busy`} />
-						</MenuItem>
-						<MenuItem
-							data={{
-								action: "set_presence",
-								presence: Users.Presence.Invisible,
-							}}
-							disabled={!isOnline}>
-							<div className="indicator invisible" />
-							<Text id={`app.status.invisible`} />
-						</MenuItem>
-						<LineDivider />
-						<div className="header">
-							<div className="main">
-								<MenuItem
-									data={{ action: "set_status" }}
-									disabled={!isOnline}>
-									<Text
-										id={`app.context_menu.custom_status`}
-									/>
-								</MenuItem>
-							</div>
-							{client.user!.status?.text && (
-								<IconButton>
-									<MenuItem data={{ action: "clear_status" }}>
-										<Trash size={18} />
-									</MenuItem>
-								</IconButton>
-							)}
-						</div>
-					</>
-				)}
-			</ContextMenuWithData>
-			<ContextMenuWithData
-				id="NotificationOptions"
-				onClose={contextClick}>
-				{({ channel }: { channel: Channels.Channel }) => {
-					const state = props.notifications[channel._id];
-					const actual = getNotificationState(
-						props.notifications,
-						channel,
-					);
-
-					let elements: Children[] = [
-						<MenuItem
-							data={{
-								action: "set_notification_state",
-								key: channel._id,
-							}}>
-							<Text
-								id={`app.main.channel.notifications.default`}
-							/>
-							<div className="tip">
-								{state !== undefined && <Square size={20} />}
-								{state === undefined && (
-									<CheckSquare size={20} />
-								)}
-							</div>
-						</MenuItem>,
-					];
-
-					function generate(key: string, icon: Children) {
-						elements.push(
-							<MenuItem
-								data={{
-									action: "set_notification_state",
-									key: channel._id,
-									state: key,
-								}}>
-								{icon}
-								<Text
-									id={`app.main.channel.notifications.${key}`}
-								/>
-								{state === undefined && actual === key && (
-									<div className="tip">
-										<LeftArrowAlt size={20} />
-									</div>
-								)}
-								{state === key && (
-									<div className="tip">
-										<Check size={20} />
-									</div>
-								)}
-							</MenuItem>,
-						);
-					}
-
-					generate("all", <Bell size={24} />);
-					generate("mention", <At size={24} />);
-					generate("muted", <BellOff size={24} />);
-					generate("none", <Block size={24} />);
-
-					return elements;
-				}}
-			</ContextMenuWithData>
-		</>
-	);
+    const { openScreen, writeClipboard } = useIntermediate();
+    const client = useContext(AppContext);
+    const userId = client.user!._id;
+    const status = useContext(StatusContext);
+    const isOnline = status === ClientStatus.ONLINE;
+    const history = useHistory();
+
+    function contextClick(data?: Action) {
+        if (typeof data === "undefined") return;
+
+        (async () => {
+            switch (data.action) {
+                case "copy_id":
+                    writeClipboard(data.id);
+                    break;
+                case "copy_selection":
+                    writeClipboard(document.getSelection()?.toString() ?? "");
+                    break;
+                case "mark_as_read":
+                    {
+                        if (
+                            data.channel.channel_type === "SavedMessages" ||
+                            data.channel.channel_type === "VoiceChannel"
+                        )
+                            return;
+
+                        let message =
+                            data.channel.channel_type === "TextChannel"
+                                ? data.channel.last_message
+                                : data.channel.last_message._id;
+                        dispatch({
+                            type: "UNREADS_MARK_READ",
+                            channel: data.channel._id,
+                            message,
+                        });
+
+                        client.req(
+                            "PUT",
+                            `/channels/${data.channel._id}/ack/${message}` as "/channels/id/ack/id",
+                        );
+                    }
+                    break;
+
+                case "retry_message":
+                    {
+                        const nonce = data.message.id;
+                        const fail = (error: any) =>
+                            dispatch({
+                                type: "QUEUE_FAIL",
+                                nonce,
+                                error,
+                            });
+
+                        client.channels
+                            .sendMessage(data.message.channel, {
+                                nonce: data.message.id,
+                                content: data.message.data.content as string,
+                                replies: data.message.data.replies,
+                            })
+                            .catch(fail);
+
+                        dispatch({
+                            type: "QUEUE_START",
+                            nonce,
+                        });
+                    }
+                    break;
+
+                case "cancel_message":
+                    {
+                        dispatch({
+                            type: "QUEUE_REMOVE",
+                            nonce: data.message.id,
+                        });
+                    }
+                    break;
+
+                case "mention":
+                    {
+                        internalEmit(
+                            "MessageBox",
+                            "append",
+                            `<@${data.user}>`,
+                            "mention",
+                        );
+                    }
+                    break;
+
+                case "copy_text":
+                    writeClipboard(data.content);
+                    break;
+
+                case "reply_message":
+                    {
+                        internalEmit("ReplyBar", "add", data.id);
+                    }
+                    break;
+
+                case "quote_message":
+                    {
+                        internalEmit(
+                            "MessageBox",
+                            "append",
+                            data.content,
+                            "quote",
+                        );
+                    }
+                    break;
+
+                case "edit_message":
+                    {
+                        internalEmit(
+                            "MessageRenderer",
+                            "edit_message",
+                            data.id,
+                        );
+                    }
+                    break;
+
+                case "open_file":
+                    {
+                        window
+                            .open(
+                                client.generateFileURL(data.attachment),
+                                "_blank",
+                            )
+                            ?.focus();
+                    }
+                    break;
+
+                case "save_file":
+                    {
+                        window.open(
+                            // ! FIXME: do this from revolt.js
+                            client
+                                .generateFileURL(data.attachment)
+                                ?.replace(
+                                    "attachments",
+                                    "attachments/download",
+                                ),
+                            "_blank",
+                        );
+                    }
+                    break;
+
+                case "copy_file_link":
+                    {
+                        const { filename } = data.attachment;
+                        writeClipboard(
+                            // ! FIXME: do from r.js
+                            client.generateFileURL(data.attachment) +
+                                `/${encodeURI(filename)}`,
+                        );
+                    }
+                    break;
+
+                case "open_link":
+                    {
+                        window.open(data.link, "_blank")?.focus();
+                    }
+                    break;
+
+                case "copy_link":
+                    {
+                        writeClipboard(data.link);
+                    }
+                    break;
+
+                case "remove_member":
+                    {
+                        client.channels.removeMember(data.channel, data.user);
+                    }
+                    break;
+
+                case "view_profile":
+                    openScreen({ id: "profile", user_id: data.user });
+                    break;
+
+                case "message_user":
+                    {
+                        const channel = await client.users.openDM(data.user);
+                        if (channel) {
+                            history.push(`/channel/${channel._id}`);
+                        }
+                    }
+                    break;
+
+                case "add_friend":
+                    {
+                        await client.users.addFriend(data.user.username);
+                    }
+                    break;
+
+                case "block_user":
+                    openScreen({
+                        id: "special_prompt",
+                        type: "block_user",
+                        target: data.user,
+                    });
+                    break;
+                case "unblock_user":
+                    await client.users.unblockUser(data.user._id);
+                    break;
+                case "remove_friend":
+                    openScreen({
+                        id: "special_prompt",
+                        type: "unfriend_user",
+                        target: data.user,
+                    });
+                    break;
+                case "cancel_friend":
+                    await client.users.removeFriend(data.user._id);
+                    break;
+
+                case "set_presence":
+                    {
+                        await client.users.editUser({
+                            status: {
+                                ...client.user?.status,
+                                presence: data.presence,
+                            },
+                        });
+                    }
+                    break;
+
+                case "set_status":
+                    openScreen({
+                        id: "special_input",
+                        type: "set_custom_status",
+                    });
+                    break;
+
+                case "clear_status":
+                    {
+                        let { text, ...status } = client.user?.status ?? {};
+                        await client.users.editUser({ status });
+                    }
+                    break;
+
+                case "leave_group":
+                case "close_dm":
+                case "leave_server":
+                case "delete_channel":
+                case "delete_server":
+                case "delete_message":
+                case "create_channel":
+                case "create_invite":
+                    // The any here is because typescript flattens the case types into a single type and type structure and specifity is lost or whatever
+                    openScreen({
+                        id: "special_prompt",
+                        type: data.action,
+                        target: data.target as any,
+                    });
+                    break;
+
+                case "ban_member":
+                case "kick_member":
+                    openScreen({
+                        id: "special_prompt",
+                        type: data.action,
+                        target: data.target,
+                        user: data.user,
+                    });
+                    break;
+
+                case "open_notification_options": {
+                    openContextMenu("NotificationOptions", {
+                        channel: data.channel,
+                    });
+                    break;
+                }
+
+                case "open_settings":
+                    history.push("/settings");
+                    break;
+                case "open_channel_settings":
+                    history.push(`/channel/${data.id}/settings`);
+                    break;
+                case "open_server_channel_settings":
+                    history.push(
+                        `/server/${data.server}/channel/${data.id}/settings`,
+                    );
+                    break;
+                case "open_server_settings":
+                    history.push(`/server/${data.id}/settings`);
+                    break;
+
+                case "set_notification_state": {
+                    const { key, state } = data;
+                    if (state) {
+                        dispatch({ type: "NOTIFICATIONS_SET", key, state });
+                    } else {
+                        dispatch({ type: "NOTIFICATIONS_REMOVE", key });
+                    }
+                    break;
+                }
+            }
+        })().catch((err) => {
+            openScreen({ id: "error", error: takeError(err) });
+        });
+    }
+
+    return (
+        <>
+            <ContextMenuWithData id="Menu" onClose={contextClick}>
+                {({
+                    user: uid,
+                    channel: cid,
+                    server: sid,
+                    message,
+                    server_list,
+                    queued,
+                    unread,
+                    contextualChannel: cxid,
+                }: ContextMenuData) => {
+                    const forceUpdate = useForceUpdate();
+                    const elements: Children[] = [];
+                    var lastDivider = false;
+
+                    function generateAction(
+                        action: Action,
+                        locale?: string,
+                        disabled?: boolean,
+                        tip?: Children,
+                    ) {
+                        lastDivider = false;
+                        elements.push(
+                            <MenuItem data={action} disabled={disabled}>
+                                <Text
+                                    id={`app.context_menu.${
+                                        locale ?? action.action
+                                    }`}
+                                />
+                                {tip && <div className="tip">{tip}</div>}
+                            </MenuItem>,
+                        );
+                    }
+
+                    function pushDivider() {
+                        if (lastDivider || elements.length === 0) return;
+                        lastDivider = true;
+                        elements.push(<LineDivider />);
+                    }
+
+                    if (server_list) {
+                        let server = useServer(server_list, forceUpdate);
+                        let permissions = useServerPermission(
+                            server_list,
+                            forceUpdate,
+                        );
+                        if (server) {
+                            if (permissions & ServerPermission.ManageChannels)
+                                generateAction({
+                                    action: "create_channel",
+                                    target: server,
+                                });
+                            if (permissions & ServerPermission.ManageServer)
+                                generateAction({
+                                    action: "open_server_settings",
+                                    id: server_list,
+                                });
+                        }
+
+                        return elements;
+                    }
+
+                    if (document.getSelection()?.toString().length ?? 0 > 0) {
+                        generateAction(
+                            { action: "copy_selection" },
+                            undefined,
+                            undefined,
+                            <Text id="shortcuts.ctrlc" />,
+                        );
+                        pushDivider();
+                    }
+
+                    const channel = useChannel(cid, forceUpdate);
+                    const contextualChannel = useChannel(cxid, forceUpdate);
+                    const targetChannel = channel ?? contextualChannel;
+
+                    const user = useUser(uid, forceUpdate);
+                    const serverChannel =
+                        targetChannel &&
+                        (targetChannel.channel_type === "TextChannel" ||
+                            targetChannel.channel_type === "VoiceChannel")
+                            ? targetChannel
+                            : undefined;
+                    const server = useServer(
+                        serverChannel ? serverChannel.server : sid,
+                        forceUpdate,
+                    );
+
+                    const channelPermissions = targetChannel
+                        ? useChannelPermission(targetChannel._id, forceUpdate)
+                        : 0;
+                    const serverPermissions = server
+                        ? useServerPermission(server._id, forceUpdate)
+                        : serverChannel
+                        ? useServerPermission(serverChannel.server, forceUpdate)
+                        : 0;
+                    const userPermissions = user
+                        ? useUserPermission(user._id, forceUpdate)
+                        : 0;
+
+                    if (channel && unread) {
+                        generateAction({ action: "mark_as_read", channel });
+                    }
+
+                    if (contextualChannel) {
+                        if (user && user._id !== userId) {
+                            generateAction({
+                                action: "mention",
+                                user: user._id,
+                            });
+
+                            pushDivider();
+                        }
+                    }
+
+                    if (user) {
+                        let actions: Action["action"][];
+                        switch (user.relationship) {
+                            case Users.Relationship.User:
+                                actions = [];
+                                break;
+                            case Users.Relationship.Friend:
+                                actions = ["remove_friend", "block_user"];
+                                break;
+                            case Users.Relationship.Incoming:
+                                actions = [
+                                    "add_friend",
+                                    "cancel_friend",
+                                    "block_user",
+                                ];
+                                break;
+                            case Users.Relationship.Outgoing:
+                                actions = ["cancel_friend", "block_user"];
+                                break;
+                            case Users.Relationship.Blocked:
+                                actions = ["unblock_user"];
+                                break;
+                            case Users.Relationship.BlockedOther:
+                                actions = ["block_user"];
+                                break;
+                            case Users.Relationship.None:
+                            default:
+                                actions = ["add_friend", "block_user"];
+                        }
+
+                        if (userPermissions & UserPermission.ViewProfile) {
+                            generateAction({
+                                action: "view_profile",
+                                user: user._id,
+                            });
+                        }
+
+                        if (
+                            user._id !== userId &&
+                            userPermissions & UserPermission.SendMessage
+                        ) {
+                            generateAction({
+                                action: "message_user",
+                                user: user._id,
+                            });
+                        }
+
+                        for (let i = 0; i < actions.length; i++) {
+                            // The any here is because typescript can't determine that user the actions are linked together correctly
+                            generateAction({ action: actions[i] as any, user });
+                        }
+                    }
+
+                    if (contextualChannel) {
+                        if (contextualChannel.channel_type === "Group" && uid) {
+                            if (
+                                contextualChannel.owner === userId &&
+                                userId !== uid
+                            ) {
+                                generateAction({
+                                    action: "remove_member",
+                                    channel: contextualChannel._id,
+                                    user: uid,
+                                });
+                            }
+                        }
+
+                        if (
+                            server &&
+                            uid &&
+                            userId !== uid &&
+                            uid !== server.owner
+                        ) {
+                            if (
+                                serverPermissions & ServerPermission.KickMembers
+                            )
+                                generateAction({
+                                    action: "kick_member",
+                                    target: server,
+                                    user: uid,
+                                });
+
+                            if (serverPermissions & ServerPermission.BanMembers)
+                                generateAction({
+                                    action: "ban_member",
+                                    target: server,
+                                    user: uid,
+                                });
+                        }
+                    }
+
+                    if (queued) {
+                        generateAction({
+                            action: "retry_message",
+                            message: queued,
+                        });
+
+                        generateAction({
+                            action: "cancel_message",
+                            message: queued,
+                        });
+                    }
+
+                    if (message && !queued) {
+                        generateAction({
+                            action: "reply_message",
+                            id: message._id,
+                        });
+
+                        if (
+                            typeof message.content === "string" &&
+                            message.content.length > 0
+                        ) {
+                            generateAction({
+                                action: "quote_message",
+                                content: message.content,
+                            });
+
+                            generateAction({
+                                action: "copy_text",
+                                content: message.content,
+                            });
+                        }
+
+                        if (message.author === userId) {
+                            generateAction({
+                                action: "edit_message",
+                                id: message._id,
+                            });
+                        }
+
+                        if (
+                            message.author === userId ||
+                            channelPermissions &
+                                ChannelPermission.ManageMessages
+                        ) {
+                            generateAction({
+                                action: "delete_message",
+                                target: message,
+                            });
+                        }
+
+                        if (message.attachments) {
+                            pushDivider();
+                            const { metadata } = message.attachments[0];
+                            const { type } = metadata;
+
+                            generateAction(
+                                {
+                                    action: "open_file",
+                                    attachment: message.attachments[0],
+                                },
+                                type === "Image"
+                                    ? "open_image"
+                                    : type === "Video"
+                                    ? "open_video"
+                                    : "open_file",
+                            );
+
+                            generateAction(
+                                {
+                                    action: "save_file",
+                                    attachment: message.attachments[0],
+                                },
+                                type === "Image"
+                                    ? "save_image"
+                                    : type === "Video"
+                                    ? "save_video"
+                                    : "save_file",
+                            );
+
+                            generateAction(
+                                {
+                                    action: "copy_file_link",
+                                    attachment: message.attachments[0],
+                                },
+                                "copy_link",
+                            );
+                        }
+
+                        if (document.activeElement?.tagName === "A") {
+                            let link =
+                                document.activeElement.getAttribute("href");
+                            if (link) {
+                                pushDivider();
+                                generateAction({ action: "open_link", link });
+                                generateAction({ action: "copy_link", link });
+                            }
+                        }
+                    }
+
+                    let id = sid ?? cid ?? uid ?? message?._id;
+                    if (id) {
+                        pushDivider();
+
+                        if (channel) {
+                            if (channel.channel_type !== "VoiceChannel") {
+                                generateAction(
+                                    {
+                                        action: "open_notification_options",
+                                        channel,
+                                    },
+                                    undefined,
+                                    undefined,
+                                    <ChevronRight size={24} />,
+                                );
+                            }
+
+                            switch (channel.channel_type) {
+                                case "Group":
+                                    // ! generateAction({ action: "create_invite", target: channel }); FIXME: add support for group invites
+                                    generateAction(
+                                        {
+                                            action: "open_channel_settings",
+                                            id: channel._id,
+                                        },
+                                        "open_group_settings",
+                                    );
+                                    generateAction(
+                                        {
+                                            action: "leave_group",
+                                            target: channel,
+                                        },
+                                        "leave_group",
+                                    );
+                                    break;
+                                case "DirectMessage":
+                                    generateAction({
+                                        action: "close_dm",
+                                        target: channel,
+                                    });
+                                    break;
+                                case "TextChannel":
+                                case "VoiceChannel":
+                                    // ! FIXME: add permission for invites
+                                    generateAction({
+                                        action: "create_invite",
+                                        target: channel,
+                                    });
+
+                                    if (
+                                        serverPermissions &
+                                        ServerPermission.ManageServer
+                                    )
+                                        generateAction(
+                                            {
+                                                action: "open_server_channel_settings",
+                                                server: channel.server,
+                                                id: channel._id,
+                                            },
+                                            "open_channel_settings",
+                                        );
+
+                                    if (
+                                        serverPermissions &
+                                        ServerPermission.ManageChannels
+                                    )
+                                        generateAction({
+                                            action: "delete_channel",
+                                            target: channel,
+                                        });
+
+                                    break;
+                            }
+                        }
+
+                        if (sid && server) {
+                            if (
+                                serverPermissions &
+                                ServerPermission.ManageServer
+                            )
+                                generateAction(
+                                    {
+                                        action: "open_server_settings",
+                                        id: server._id,
+                                    },
+                                    "open_server_settings",
+                                );
+
+                            if (userId === server.owner) {
+                                generateAction(
+                                    { action: "delete_server", target: server },
+                                    "delete_server",
+                                );
+                            } else {
+                                generateAction(
+                                    { action: "leave_server", target: server },
+                                    "leave_server",
+                                );
+                            }
+                        }
+
+                        generateAction(
+                            { action: "copy_id", id },
+                            sid
+                                ? "copy_sid"
+                                : cid
+                                ? "copy_cid"
+                                : message
+                                ? "copy_mid"
+                                : "copy_uid",
+                        );
+                    }
+
+                    return elements;
+                }}
+            </ContextMenuWithData>
+            <ContextMenuWithData
+                id="Status"
+                onClose={contextClick}
+                className="Status">
+                {() => (
+                    <>
+                        <div className="header">
+                            <div className="main">
+                                <div>@{client.user!.username}</div>
+                                <div className="status">
+                                    <UserStatus user={client.user!} />
+                                </div>
+                            </div>
+                            <IconButton>
+                                <MenuItem data={{ action: "open_settings" }}>
+                                    <Cog size={18} />
+                                </MenuItem>
+                            </IconButton>
+                        </div>
+                        <LineDivider />
+                        <MenuItem
+                            data={{
+                                action: "set_presence",
+                                presence: Users.Presence.Online,
+                            }}
+                            disabled={!isOnline}>
+                            <div className="indicator online" />
+                            <Text id={`app.status.online`} />
+                        </MenuItem>
+                        <MenuItem
+                            data={{
+                                action: "set_presence",
+                                presence: Users.Presence.Idle,
+                            }}
+                            disabled={!isOnline}>
+                            <div className="indicator idle" />
+                            <Text id={`app.status.idle`} />
+                        </MenuItem>
+                        <MenuItem
+                            data={{
+                                action: "set_presence",
+                                presence: Users.Presence.Busy,
+                            }}
+                            disabled={!isOnline}>
+                            <div className="indicator busy" />
+                            <Text id={`app.status.busy`} />
+                        </MenuItem>
+                        <MenuItem
+                            data={{
+                                action: "set_presence",
+                                presence: Users.Presence.Invisible,
+                            }}
+                            disabled={!isOnline}>
+                            <div className="indicator invisible" />
+                            <Text id={`app.status.invisible`} />
+                        </MenuItem>
+                        <LineDivider />
+                        <div className="header">
+                            <div className="main">
+                                <MenuItem
+                                    data={{ action: "set_status" }}
+                                    disabled={!isOnline}>
+                                    <Text
+                                        id={`app.context_menu.custom_status`}
+                                    />
+                                </MenuItem>
+                            </div>
+                            {client.user!.status?.text && (
+                                <IconButton>
+                                    <MenuItem data={{ action: "clear_status" }}>
+                                        <Trash size={18} />
+                                    </MenuItem>
+                                </IconButton>
+                            )}
+                        </div>
+                    </>
+                )}
+            </ContextMenuWithData>
+            <ContextMenuWithData
+                id="NotificationOptions"
+                onClose={contextClick}>
+                {({ channel }: { channel: Channels.Channel }) => {
+                    const state = props.notifications[channel._id];
+                    const actual = getNotificationState(
+                        props.notifications,
+                        channel,
+                    );
+
+                    let elements: Children[] = [
+                        <MenuItem
+                            data={{
+                                action: "set_notification_state",
+                                key: channel._id,
+                            }}>
+                            <Text
+                                id={`app.main.channel.notifications.default`}
+                            />
+                            <div className="tip">
+                                {state !== undefined && <Square size={20} />}
+                                {state === undefined && (
+                                    <CheckSquare size={20} />
+                                )}
+                            </div>
+                        </MenuItem>,
+                    ];
+
+                    function generate(key: string, icon: Children) {
+                        elements.push(
+                            <MenuItem
+                                data={{
+                                    action: "set_notification_state",
+                                    key: channel._id,
+                                    state: key,
+                                }}>
+                                {icon}
+                                <Text
+                                    id={`app.main.channel.notifications.${key}`}
+                                />
+                                {state === undefined && actual === key && (
+                                    <div className="tip">
+                                        <LeftArrowAlt size={20} />
+                                    </div>
+                                )}
+                                {state === key && (
+                                    <div className="tip">
+                                        <Check size={20} />
+                                    </div>
+                                )}
+                            </MenuItem>,
+                        );
+                    }
+
+                    generate("all", <Bell size={24} />);
+                    generate("mention", <At size={24} />);
+                    generate("muted", <BellOff size={24} />);
+                    generate("none", <Block size={24} />);
+
+                    return elements;
+                }}
+            </ContextMenuWithData>
+        </>
+    );
 }
 
 export default connectState(ContextMenus, (state) => {
-	return {
-		notifications: state.notifications,
-	};
+    return {
+        notifications: state.notifications,
+    };
 });
diff --git a/src/lib/PaintCounter.tsx b/src/lib/PaintCounter.tsx
index 15cd5c3..b346ec4 100644
--- a/src/lib/PaintCounter.tsx
+++ b/src/lib/PaintCounter.tsx
@@ -3,20 +3,20 @@ import { useState } from "preact/hooks";
 const counts: { [key: string]: number } = {};
 
 export default function PaintCounter({
-	small,
-	always,
+    small,
+    always,
 }: {
-	small?: boolean;
-	always?: boolean;
+    small?: boolean;
+    always?: boolean;
 }) {
-	if (import.meta.env.PROD && !always) return null;
+    if (import.meta.env.PROD && !always) return null;
 
-	const [uniqueId] = useState("" + Math.random());
-	const count = counts[uniqueId] ?? 0;
-	counts[uniqueId] = count + 1;
-	return (
-		<div style={{ textAlign: "center", fontSize: "0.8em" }}>
-			{small ? <>P: {count + 1}</> : <>Painted {count + 1} time(s).</>}
-		</div>
-	);
+    const [uniqueId] = useState("" + Math.random());
+    const count = counts[uniqueId] ?? 0;
+    counts[uniqueId] = count + 1;
+    return (
+        <div style={{ textAlign: "center", fontSize: "0.8em" }}>
+            {small ? <>P: {count + 1}</> : <>Painted {count + 1} time(s).</>}
+        </div>
+    );
 }
diff --git a/src/lib/TextAreaAutoSize.tsx b/src/lib/TextAreaAutoSize.tsx
index a01cc57..de9022b 100644
--- a/src/lib/TextAreaAutoSize.tsx
+++ b/src/lib/TextAreaAutoSize.tsx
@@ -1,113 +1,113 @@
 import { useEffect, useRef } from "preact/hooks";
 
 import TextArea, {
-	DEFAULT_LINE_HEIGHT,
-	DEFAULT_TEXT_AREA_PADDING,
-	TextAreaProps,
-	TEXT_AREA_BORDER_WIDTH,
+    DEFAULT_LINE_HEIGHT,
+    DEFAULT_TEXT_AREA_PADDING,
+    TextAreaProps,
+    TEXT_AREA_BORDER_WIDTH,
 } from "../components/ui/TextArea";
 
 import { internalSubscribe } from "./eventEmitter";
 import { isTouchscreenDevice } from "./isTouchscreenDevice";
 
 type TextAreaAutoSizeProps = Omit<
-	JSX.HTMLAttributes<HTMLTextAreaElement>,
-	"style" | "value"
+    JSX.HTMLAttributes<HTMLTextAreaElement>,
+    "style" | "value"
 > &
-	TextAreaProps & {
-		forceFocus?: boolean;
-		autoFocus?: boolean;
-		minHeight?: number;
-		maxRows?: number;
-		value: string;
+    TextAreaProps & {
+        forceFocus?: boolean;
+        autoFocus?: boolean;
+        minHeight?: number;
+        maxRows?: number;
+        value: string;
 
-		id?: string;
-	};
+        id?: string;
+    };
 
 export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
-	const {
-		autoFocus,
-		minHeight,
-		maxRows,
-		value,
-		padding,
-		lineHeight,
-		hideBorder,
-		forceFocus,
-		children,
-		as,
-		...textAreaProps
-	} = props;
-	const line = lineHeight ?? DEFAULT_LINE_HEIGHT;
-
-	const heightPadding =
-		((padding ?? DEFAULT_TEXT_AREA_PADDING) +
-			(hideBorder ? 0 : TEXT_AREA_BORDER_WIDTH)) *
-		2;
-	const height = Math.max(
-		Math.min(value.split("\n").length, maxRows ?? Infinity) * line +
-			heightPadding,
-		minHeight ?? 0,
-	);
-
-	const ref = useRef<HTMLTextAreaElement>();
-
-	useEffect(() => {
-		if (isTouchscreenDevice) return;
-		autoFocus && ref.current.focus();
-	}, [value]);
-
-	const inputSelected = () =>
-		["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? "");
-
-	useEffect(() => {
-		if (forceFocus) {
-			ref.current.focus();
-		}
-
-		if (isTouchscreenDevice) return;
-		if (autoFocus && !inputSelected()) {
-			ref.current.focus();
-		}
-
-		// ? if you are wondering what this is
-		// ? it is a quick and dirty hack to fix
-		// ? value not setting correctly
-		// ? I have no clue what's going on
-		ref.current.value = value;
-
-		if (!autoFocus) return;
-		function keyDown(e: KeyboardEvent) {
-			if ((e.ctrlKey && e.key !== "v") || e.altKey || e.metaKey) return;
-			if (e.key.length !== 1) return;
-			if (ref && !inputSelected()) {
-				ref.current.focus();
-			}
-		}
-
-		document.body.addEventListener("keydown", keyDown);
-		return () => document.body.removeEventListener("keydown", keyDown);
-	}, [ref]);
-
-	useEffect(() => {
-		function focus(id: string) {
-			if (id === props.id) {
-				ref.current.focus();
-			}
-		}
-
-		return internalSubscribe("TextArea", "focus", focus);
-	}, [ref]);
-
-	return (
-		<TextArea
-			ref={ref}
-			value={value}
-			padding={padding}
-			style={{ height }}
-			hideBorder={hideBorder}
-			lineHeight={lineHeight}
-			{...textAreaProps}
-		/>
-	);
+    const {
+        autoFocus,
+        minHeight,
+        maxRows,
+        value,
+        padding,
+        lineHeight,
+        hideBorder,
+        forceFocus,
+        children,
+        as,
+        ...textAreaProps
+    } = props;
+    const line = lineHeight ?? DEFAULT_LINE_HEIGHT;
+
+    const heightPadding =
+        ((padding ?? DEFAULT_TEXT_AREA_PADDING) +
+            (hideBorder ? 0 : TEXT_AREA_BORDER_WIDTH)) *
+        2;
+    const height = Math.max(
+        Math.min(value.split("\n").length, maxRows ?? Infinity) * line +
+            heightPadding,
+        minHeight ?? 0,
+    );
+
+    const ref = useRef<HTMLTextAreaElement>();
+
+    useEffect(() => {
+        if (isTouchscreenDevice) return;
+        autoFocus && ref.current.focus();
+    }, [value]);
+
+    const inputSelected = () =>
+        ["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? "");
+
+    useEffect(() => {
+        if (forceFocus) {
+            ref.current.focus();
+        }
+
+        if (isTouchscreenDevice) return;
+        if (autoFocus && !inputSelected()) {
+            ref.current.focus();
+        }
+
+        // ? if you are wondering what this is
+        // ? it is a quick and dirty hack to fix
+        // ? value not setting correctly
+        // ? I have no clue what's going on
+        ref.current.value = value;
+
+        if (!autoFocus) return;
+        function keyDown(e: KeyboardEvent) {
+            if ((e.ctrlKey && e.key !== "v") || e.altKey || e.metaKey) return;
+            if (e.key.length !== 1) return;
+            if (ref && !inputSelected()) {
+                ref.current.focus();
+            }
+        }
+
+        document.body.addEventListener("keydown", keyDown);
+        return () => document.body.removeEventListener("keydown", keyDown);
+    }, [ref]);
+
+    useEffect(() => {
+        function focus(id: string) {
+            if (id === props.id) {
+                ref.current.focus();
+            }
+        }
+
+        return internalSubscribe("TextArea", "focus", focus);
+    }, [ref]);
+
+    return (
+        <TextArea
+            ref={ref}
+            value={value}
+            padding={padding}
+            style={{ height }}
+            hideBorder={hideBorder}
+            lineHeight={lineHeight}
+            {...textAreaProps}
+        />
+    );
 }
diff --git a/src/lib/conversion.ts b/src/lib/conversion.ts
index 263dd5b..92eeb12 100644
--- a/src/lib/conversion.ts
+++ b/src/lib/conversion.ts
@@ -1,9 +1,9 @@
 export function urlBase64ToUint8Array(base64String: string) {
-	const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
-	const base64 = (base64String + padding)
-		.replace(/\-/g, "+")
-		.replace(/_/g, "/");
-	const rawData = window.atob(base64);
+    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
+    const base64 = (base64String + padding)
+        .replace(/\-/g, "+")
+        .replace(/_/g, "/");
+    const rawData = window.atob(base64);
 
-	return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
+    return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
 }
diff --git a/src/lib/debounce.ts b/src/lib/debounce.ts
index 2b7d63a..b65292b 100644
--- a/src/lib/debounce.ts
+++ b/src/lib/debounce.ts
@@ -1,15 +1,15 @@
 export function debounce(cb: Function, duration: number) {
-	// Store the timer variable.
-	let timer: NodeJS.Timeout;
-	// This function is given to React.
-	return (...args: any[]) => {
-		// Get rid of the old timer.
-		clearTimeout(timer);
-		// Set a new timer.
-		timer = setTimeout(() => {
-			// Instead calling the new function.
-			// (with the newer data)
-			cb(...args);
-		}, duration);
-	};
+    // Store the timer variable.
+    let timer: NodeJS.Timeout;
+    // This function is given to React.
+    return (...args: any[]) => {
+        // Get rid of the old timer.
+        clearTimeout(timer);
+        // Set a new timer.
+        timer = setTimeout(() => {
+            // Instead calling the new function.
+            // (with the newer data)
+            cb(...args);
+        }, duration);
+    };
 }
diff --git a/src/lib/eventEmitter.ts b/src/lib/eventEmitter.ts
index 131f1bf..e2881c8 100644
--- a/src/lib/eventEmitter.ts
+++ b/src/lib/eventEmitter.ts
@@ -3,16 +3,16 @@ import EventEmitter from "eventemitter3";
 export const InternalEvent = new EventEmitter();
 
 export function internalSubscribe(
-	ns: string,
-	event: string,
-	fn: (...args: any[]) => void,
+    ns: string,
+    event: string,
+    fn: (...args: any[]) => void,
 ) {
-	InternalEvent.addListener(ns + "/" + event, fn);
-	return () => InternalEvent.removeListener(ns + "/" + event, fn);
+    InternalEvent.addListener(ns + "/" + event, fn);
+    return () => InternalEvent.removeListener(ns + "/" + event, fn);
 }
 
 export function internalEmit(ns: string, event: string, ...args: any[]) {
-	InternalEvent.emit(ns + "/" + event, ...args);
+    InternalEvent.emit(ns + "/" + event, ...args);
 }
 
 // Event structure: namespace/event
diff --git a/src/lib/fileSize.ts b/src/lib/fileSize.ts
index 9caea0c..c6bba81 100644
--- a/src/lib/fileSize.ts
+++ b/src/lib/fileSize.ts
@@ -1,9 +1,9 @@
 export function determineFileSize(size: number) {
-	if (size > 1e6) {
-		return `${(size / 1e6).toFixed(2)} MB`;
-	} else if (size > 1e3) {
-		return `${(size / 1e3).toFixed(2)} KB`;
-	}
+    if (size > 1e6) {
+        return `${(size / 1e6).toFixed(2)} MB`;
+    } else if (size > 1e3) {
+        return `${(size / 1e3).toFixed(2)} KB`;
+    }
 
-	return `${size} B`;
+    return `${size} B`;
 }
diff --git a/src/lib/i18n.tsx b/src/lib/i18n.tsx
index e8ca877..987f3fa 100644
--- a/src/lib/i18n.tsx
+++ b/src/lib/i18n.tsx
@@ -4,59 +4,59 @@ import { useContext } from "preact/hooks";
 import { Children } from "../types/Preact";
 
 interface Fields {
-	[key: string]: Children;
+    [key: string]: Children;
 }
 
 interface Props {
-	id: string;
-	fields: Fields;
+    id: string;
+    fields: Fields;
 }
 
 export interface IntlType {
-	intl: {
-		dictionary: {
-			[key: string]: Object | string;
-		};
-	};
+    intl: {
+        dictionary: {
+            [key: string]: Object | string;
+        };
+    };
 }
 
 // This will exhibit O(2^n) behaviour.
 function recursiveReplaceFields(input: string, fields: Fields) {
-	const key = Object.keys(fields)[0];
-	if (key) {
-		const { [key]: field, ...restOfFields } = fields;
-		if (typeof field === "undefined") return [input];
-
-		const values: (Children | string[])[] = input
-			.split(`{{${key}}}`)
-			.map((v) => recursiveReplaceFields(v, restOfFields));
-
-		for (let i = values.length - 1; i > 0; i -= 2) {
-			values.splice(i, 0, field);
-		}
-
-		return values.flat();
-	} else {
-		// base case
-		return [input];
-	}
+    const key = Object.keys(fields)[0];
+    if (key) {
+        const { [key]: field, ...restOfFields } = fields;
+        if (typeof field === "undefined") return [input];
+
+        const values: (Children | string[])[] = input
+            .split(`{{${key}}}`)
+            .map((v) => recursiveReplaceFields(v, restOfFields));
+
+        for (let i = values.length - 1; i > 0; i -= 2) {
+            values.splice(i, 0, field);
+        }
+
+        return values.flat();
+    } else {
+        // base case
+        return [input];
+    }
 }
 
 export function TextReact({ id, fields }: Props) {
-	const { intl } = useContext(IntlContext) as unknown as IntlType;
+    const { intl } = useContext(IntlContext) as unknown as IntlType;
 
-	const path = id.split(".");
-	let entry = intl.dictionary[path.shift()!];
-	for (let key of path) {
-		// @ts-expect-error
-		entry = entry[key];
-	}
+    const path = id.split(".");
+    let entry = intl.dictionary[path.shift()!];
+    for (let key of path) {
+        // @ts-expect-error
+        entry = entry[key];
+    }
 
-	return <>{recursiveReplaceFields(entry as string, fields)}</>;
+    return <>{recursiveReplaceFields(entry as string, fields)}</>;
 }
 
 export function useTranslation() {
-	const { intl } = useContext(IntlContext) as unknown as IntlType;
-	return (id: string, fields?: Object, plural?: number, fallback?: string) =>
-		translate(id, "", intl.dictionary, fields, plural, fallback);
+    const { intl } = useContext(IntlContext) as unknown as IntlType;
+    return (id: string, fields?: Object, plural?: number, fallback?: string) =>
+        translate(id, "", intl.dictionary, fields, plural, fallback);
 }
diff --git a/src/lib/isTouchscreenDevice.ts b/src/lib/isTouchscreenDevice.ts
index 6f2dc0a..ee67499 100644
--- a/src/lib/isTouchscreenDevice.ts
+++ b/src/lib/isTouchscreenDevice.ts
@@ -1,8 +1,8 @@
 import { isDesktop, isMobile, isTablet } from "react-device-detect";
 
 export const isTouchscreenDevice =
-	isDesktop && !isTablet
-		? false
-		: (typeof window !== "undefined"
-				? navigator.maxTouchPoints > 0
-				: false) || isMobile;
+    isDesktop && !isTablet
+        ? false
+        : (typeof window !== "undefined"
+              ? navigator.maxTouchPoints > 0
+              : false) || isMobile;
diff --git a/src/lib/renderer/Singleton.ts b/src/lib/renderer/Singleton.ts
index fd71d19..f95e8df 100644
--- a/src/lib/renderer/Singleton.ts
+++ b/src/lib/renderer/Singleton.ts
@@ -9,198 +9,198 @@ import { RendererRoutines, RenderState, ScrollState } from "./types";
 export const SMOOTH_SCROLL_ON_RECEIVE = false;
 
 export class SingletonRenderer extends EventEmitter3 {
-	client?: Client;
-	channel?: string;
-	state: RenderState;
-	currentRenderer: RendererRoutines;
-
-	stale = false;
-	fetchingTop = false;
-	fetchingBottom = false;
-
-	constructor() {
-		super();
-
-		this.receive = this.receive.bind(this);
-		this.edit = this.edit.bind(this);
-		this.delete = this.delete.bind(this);
-
-		this.state = { type: "LOADING" };
-		this.currentRenderer = SimpleRenderer;
-	}
-
-	private receive(message: Message) {
-		this.currentRenderer.receive(this, message);
-	}
-
-	private edit(id: string, patch: Partial<Message>) {
-		this.currentRenderer.edit(this, id, patch);
-	}
-
-	private delete(id: string) {
-		this.currentRenderer.delete(this, id);
-	}
-
-	subscribe(client: Client) {
-		if (this.client) {
-			this.client.removeListener("message", this.receive);
-			this.client.removeListener("message/update", this.edit);
-			this.client.removeListener("message/delete", this.delete);
-		}
-
-		this.client = client;
-		client.addListener("message", this.receive);
-		client.addListener("message/update", this.edit);
-		client.addListener("message/delete", this.delete);
-	}
-
-	private setStateUnguarded(state: RenderState, scroll?: ScrollState) {
-		this.state = state;
-		this.emit("state", state);
-
-		if (scroll) {
-			this.emit("scroll", scroll);
-		}
-	}
-
-	setState(id: string, state: RenderState, scroll?: ScrollState) {
-		if (id !== this.channel) return;
-		this.setStateUnguarded(state, scroll);
-	}
-
-	markStale() {
-		this.stale = true;
-	}
-
-	async init(id: string) {
-		this.channel = id;
-		this.stale = false;
-		this.setStateUnguarded({ type: "LOADING" });
-		await this.currentRenderer.init(this, id);
-	}
-
-	async reloadStale(id: string) {
-		if (this.stale) {
-			this.stale = false;
-			await this.init(id);
-		}
-	}
-
-	async loadTop(ref?: HTMLDivElement) {
-		if (this.fetchingTop) return;
-		this.fetchingTop = true;
-
-		function generateScroll(end: string): ScrollState {
-			if (ref) {
-				let heightRemoved = 0;
-				let messageContainer = ref.children[0];
-				if (messageContainer) {
-					for (let child of Array.from(messageContainer.children)) {
-						// If this child has a ulid.
-						if (child.id?.length === 26) {
-							// Check whether it was removed.
-							if (child.id.localeCompare(end) === 1) {
-								heightRemoved +=
-									child.clientHeight +
-									// We also need to take into account the top margin of the container.
-									parseInt(
-										window
-											.getComputedStyle(child)
-											.marginTop.slice(0, -2),
-									);
-							}
-						}
-					}
-				}
-
-				return {
-					type: "OffsetTop",
-					previousHeight: ref.scrollHeight - heightRemoved,
-				};
-			} else {
-				return {
-					type: "OffsetTop",
-					previousHeight: 0,
-				};
-			}
-		}
-
-		await this.currentRenderer.loadTop(this, generateScroll);
-
-		// Allow state updates to propagate.
-		setTimeout(() => (this.fetchingTop = false), 0);
-	}
-
-	async loadBottom(ref?: HTMLDivElement) {
-		if (this.fetchingBottom) return;
-		this.fetchingBottom = true;
-
-		function generateScroll(start: string): ScrollState {
-			if (ref) {
-				let heightRemoved = 0;
-				let messageContainer = ref.children[0];
-				if (messageContainer) {
-					for (let child of Array.from(messageContainer.children)) {
-						// If this child has a ulid.
-						if (child.id?.length === 26) {
-							// Check whether it was removed.
-							if (child.id.localeCompare(start) === -1) {
-								heightRemoved +=
-									child.clientHeight +
-									// We also need to take into account the top margin of the container.
-									parseInt(
-										window
-											.getComputedStyle(child)
-											.marginTop.slice(0, -2),
-									);
-							}
-						}
-					}
-				}
-
-				return {
-					type: "ScrollTop",
-					y: ref.scrollTop - heightRemoved,
-				};
-			} else {
-				return {
-					type: "ScrollToBottom",
-				};
-			}
-		}
-
-		await this.currentRenderer.loadBottom(this, generateScroll);
-
-		// Allow state updates to propagate.
-		setTimeout(() => (this.fetchingBottom = false), 0);
-	}
-
-	async jumpToBottom(id: string, smooth: boolean) {
-		if (id !== this.channel) return;
-		if (this.state.type === "RENDER" && this.state.atBottom) {
-			this.emit("scroll", { type: "ScrollToBottom", smooth });
-		} else {
-			await this.currentRenderer.init(this, id, true);
-		}
-	}
+    client?: Client;
+    channel?: string;
+    state: RenderState;
+    currentRenderer: RendererRoutines;
+
+    stale = false;
+    fetchingTop = false;
+    fetchingBottom = false;
+
+    constructor() {
+        super();
+
+        this.receive = this.receive.bind(this);
+        this.edit = this.edit.bind(this);
+        this.delete = this.delete.bind(this);
+
+        this.state = { type: "LOADING" };
+        this.currentRenderer = SimpleRenderer;
+    }
+
+    private receive(message: Message) {
+        this.currentRenderer.receive(this, message);
+    }
+
+    private edit(id: string, patch: Partial<Message>) {
+        this.currentRenderer.edit(this, id, patch);
+    }
+
+    private delete(id: string) {
+        this.currentRenderer.delete(this, id);
+    }
+
+    subscribe(client: Client) {
+        if (this.client) {
+            this.client.removeListener("message", this.receive);
+            this.client.removeListener("message/update", this.edit);
+            this.client.removeListener("message/delete", this.delete);
+        }
+
+        this.client = client;
+        client.addListener("message", this.receive);
+        client.addListener("message/update", this.edit);
+        client.addListener("message/delete", this.delete);
+    }
+
+    private setStateUnguarded(state: RenderState, scroll?: ScrollState) {
+        this.state = state;
+        this.emit("state", state);
+
+        if (scroll) {
+            this.emit("scroll", scroll);
+        }
+    }
+
+    setState(id: string, state: RenderState, scroll?: ScrollState) {
+        if (id !== this.channel) return;
+        this.setStateUnguarded(state, scroll);
+    }
+
+    markStale() {
+        this.stale = true;
+    }
+
+    async init(id: string) {
+        this.channel = id;
+        this.stale = false;
+        this.setStateUnguarded({ type: "LOADING" });
+        await this.currentRenderer.init(this, id);
+    }
+
+    async reloadStale(id: string) {
+        if (this.stale) {
+            this.stale = false;
+            await this.init(id);
+        }
+    }
+
+    async loadTop(ref?: HTMLDivElement) {
+        if (this.fetchingTop) return;
+        this.fetchingTop = true;
+
+        function generateScroll(end: string): ScrollState {
+            if (ref) {
+                let heightRemoved = 0;
+                let messageContainer = ref.children[0];
+                if (messageContainer) {
+                    for (let child of Array.from(messageContainer.children)) {
+                        // If this child has a ulid.
+                        if (child.id?.length === 26) {
+                            // Check whether it was removed.
+                            if (child.id.localeCompare(end) === 1) {
+                                heightRemoved +=
+                                    child.clientHeight +
+                                    // We also need to take into account the top margin of the container.
+                                    parseInt(
+                                        window
+                                            .getComputedStyle(child)
+                                            .marginTop.slice(0, -2),
+                                    );
+                            }
+                        }
+                    }
+                }
+
+                return {
+                    type: "OffsetTop",
+                    previousHeight: ref.scrollHeight - heightRemoved,
+                };
+            } else {
+                return {
+                    type: "OffsetTop",
+                    previousHeight: 0,
+                };
+            }
+        }
+
+        await this.currentRenderer.loadTop(this, generateScroll);
+
+        // Allow state updates to propagate.
+        setTimeout(() => (this.fetchingTop = false), 0);
+    }
+
+    async loadBottom(ref?: HTMLDivElement) {
+        if (this.fetchingBottom) return;
+        this.fetchingBottom = true;
+
+        function generateScroll(start: string): ScrollState {
+            if (ref) {
+                let heightRemoved = 0;
+                let messageContainer = ref.children[0];
+                if (messageContainer) {
+                    for (let child of Array.from(messageContainer.children)) {
+                        // If this child has a ulid.
+                        if (child.id?.length === 26) {
+                            // Check whether it was removed.
+                            if (child.id.localeCompare(start) === -1) {
+                                heightRemoved +=
+                                    child.clientHeight +
+                                    // We also need to take into account the top margin of the container.
+                                    parseInt(
+                                        window
+                                            .getComputedStyle(child)
+                                            .marginTop.slice(0, -2),
+                                    );
+                            }
+                        }
+                    }
+                }
+
+                return {
+                    type: "ScrollTop",
+                    y: ref.scrollTop - heightRemoved,
+                };
+            } else {
+                return {
+                    type: "ScrollToBottom",
+                };
+            }
+        }
+
+        await this.currentRenderer.loadBottom(this, generateScroll);
+
+        // Allow state updates to propagate.
+        setTimeout(() => (this.fetchingBottom = false), 0);
+    }
+
+    async jumpToBottom(id: string, smooth: boolean) {
+        if (id !== this.channel) return;
+        if (this.state.type === "RENDER" && this.state.atBottom) {
+            this.emit("scroll", { type: "ScrollToBottom", smooth });
+        } else {
+            await this.currentRenderer.init(this, id, true);
+        }
+    }
 }
 
 export const SingletonMessageRenderer = new SingletonRenderer();
 
 export function useRenderState(id: string) {
-	const [state, setState] = useState<Readonly<RenderState>>(
-		SingletonMessageRenderer.state,
-	);
-	if (typeof id === "undefined") return;
+    const [state, setState] = useState<Readonly<RenderState>>(
+        SingletonMessageRenderer.state,
+    );
+    if (typeof id === "undefined") return;
 
-	function render(state: RenderState) {
-		setState(state);
-	}
+    function render(state: RenderState) {
+        setState(state);
+    }
 
-	useEffect(() => {
-		SingletonMessageRenderer.addListener("state", render);
-		return () => SingletonMessageRenderer.removeListener("state", render);
-	}, [id]);
+    useEffect(() => {
+        SingletonMessageRenderer.addListener("state", render);
+        return () => SingletonMessageRenderer.removeListener("state", render);
+    }, [id]);
 
-	return state;
+    return state;
 }
diff --git a/src/lib/renderer/simple/SimpleRenderer.ts b/src/lib/renderer/simple/SimpleRenderer.ts
index ce4da1d..05feadc 100644
--- a/src/lib/renderer/simple/SimpleRenderer.ts
+++ b/src/lib/renderer/simple/SimpleRenderer.ts
@@ -4,180 +4,180 @@ import { SMOOTH_SCROLL_ON_RECEIVE } from "../Singleton";
 import { RendererRoutines } from "../types";
 
 export const SimpleRenderer: RendererRoutines = {
-	init: async (renderer, id, smooth) => {
-		if (renderer.client!.websocket.connected) {
-			renderer
-				.client!.channels.fetchMessagesWithUsers(id, {}, true)
-				.then(({ messages: data }) => {
-					data.reverse();
-					let messages = data.map((x) => mapMessage(x));
-					renderer.setState(
-						id,
-						{
-							type: "RENDER",
-							messages,
-							atTop: data.length < 50,
-							atBottom: true,
-						},
-						{ type: "ScrollToBottom", smooth },
-					);
-				});
-		} else {
-			renderer.setState(id, { type: "WAITING_FOR_NETWORK" });
-		}
-	},
-	receive: async (renderer, message) => {
-		if (message.channel !== renderer.channel) return;
-		if (renderer.state.type !== "RENDER") return;
-		if (renderer.state.messages.find((x) => x._id === message._id)) return;
-		if (!renderer.state.atBottom) return;
-
-		let messages = [...renderer.state.messages, mapMessage(message)];
-		let atTop = renderer.state.atTop;
-		if (messages.length > 150) {
-			messages = messages.slice(messages.length - 150);
-			atTop = false;
-		}
-
-		renderer.setState(
-			message.channel,
-			{
-				...renderer.state,
-				messages,
-				atTop,
-			},
-			{ type: "StayAtBottom", smooth: SMOOTH_SCROLL_ON_RECEIVE },
-		);
-	},
-	edit: async (renderer, id, patch) => {
-		const channel = renderer.channel;
-		if (!channel) return;
-		if (renderer.state.type !== "RENDER") return;
-
-		let messages = [...renderer.state.messages];
-		let index = messages.findIndex((x) => x._id === id);
-
-		if (index > -1) {
-			let message = { ...messages[index], ...mapMessage(patch) };
-			messages.splice(index, 1, message);
-
-			renderer.setState(
-				channel,
-				{
-					...renderer.state,
-					messages,
-				},
-				{ type: "StayAtBottom" },
-			);
-		}
-	},
-	delete: async (renderer, id) => {
-		const channel = renderer.channel;
-		if (!channel) return;
-		if (renderer.state.type !== "RENDER") return;
-
-		let messages = [...renderer.state.messages];
-		let index = messages.findIndex((x) => x._id === id);
-
-		if (index > -1) {
-			messages.splice(index, 1);
-
-			renderer.setState(
-				channel,
-				{
-					...renderer.state,
-					messages,
-				},
-				{ type: "StayAtBottom" },
-			);
-		}
-	},
-	loadTop: async (renderer, generateScroll) => {
-		const channel = renderer.channel;
-		if (!channel) return;
-
-		const state = renderer.state;
-		if (state.type !== "RENDER") return;
-		if (state.atTop) return;
-
-		const { messages: data } =
-			await renderer.client!.channels.fetchMessagesWithUsers(
-				channel,
-				{
-					before: state.messages[0]._id,
-				},
-				true,
-			);
-
-		if (data.length === 0) {
-			return renderer.setState(channel, {
-				...state,
-				atTop: true,
-			});
-		}
-
-		data.reverse();
-		let messages = [...data.map((x) => mapMessage(x)), ...state.messages];
-
-		let atTop = false;
-		if (data.length < 50) {
-			atTop = true;
-		}
-
-		let atBottom = state.atBottom;
-		if (messages.length > 150) {
-			messages = messages.slice(0, 150);
-			atBottom = false;
-		}
-
-		renderer.setState(
-			channel,
-			{ ...state, atTop, atBottom, messages },
-			generateScroll(messages[messages.length - 1]._id),
-		);
-	},
-	loadBottom: async (renderer, generateScroll) => {
-		const channel = renderer.channel;
-		if (!channel) return;
-
-		const state = renderer.state;
-		if (state.type !== "RENDER") return;
-		if (state.atBottom) return;
-
-		const { messages: data } =
-			await renderer.client!.channels.fetchMessagesWithUsers(
-				channel,
-				{
-					after: state.messages[state.messages.length - 1]._id,
-					sort: "Oldest",
-				},
-				true,
-			);
-
-		if (data.length === 0) {
-			return renderer.setState(channel, {
-				...state,
-				atBottom: true,
-			});
-		}
-
-		let messages = [...state.messages, ...data.map((x) => mapMessage(x))];
-
-		let atBottom = false;
-		if (data.length < 50) {
-			atBottom = true;
-		}
-
-		let atTop = state.atTop;
-		if (messages.length > 150) {
-			messages = messages.slice(messages.length - 150);
-			atTop = false;
-		}
-
-		renderer.setState(
-			channel,
-			{ ...state, atTop, atBottom, messages },
-			generateScroll(messages[0]._id),
-		);
-	},
+    init: async (renderer, id, smooth) => {
+        if (renderer.client!.websocket.connected) {
+            renderer
+                .client!.channels.fetchMessagesWithUsers(id, {}, true)
+                .then(({ messages: data }) => {
+                    data.reverse();
+                    let messages = data.map((x) => mapMessage(x));
+                    renderer.setState(
+                        id,
+                        {
+                            type: "RENDER",
+                            messages,
+                            atTop: data.length < 50,
+                            atBottom: true,
+                        },
+                        { type: "ScrollToBottom", smooth },
+                    );
+                });
+        } else {
+            renderer.setState(id, { type: "WAITING_FOR_NETWORK" });
+        }
+    },
+    receive: async (renderer, message) => {
+        if (message.channel !== renderer.channel) return;
+        if (renderer.state.type !== "RENDER") return;
+        if (renderer.state.messages.find((x) => x._id === message._id)) return;
+        if (!renderer.state.atBottom) return;
+
+        let messages = [...renderer.state.messages, mapMessage(message)];
+        let atTop = renderer.state.atTop;
+        if (messages.length > 150) {
+            messages = messages.slice(messages.length - 150);
+            atTop = false;
+        }
+
+        renderer.setState(
+            message.channel,
+            {
+                ...renderer.state,
+                messages,
+                atTop,
+            },
+            { type: "StayAtBottom", smooth: SMOOTH_SCROLL_ON_RECEIVE },
+        );
+    },
+    edit: async (renderer, id, patch) => {
+        const channel = renderer.channel;
+        if (!channel) return;
+        if (renderer.state.type !== "RENDER") return;
+
+        let messages = [...renderer.state.messages];
+        let index = messages.findIndex((x) => x._id === id);
+
+        if (index > -1) {
+            let message = { ...messages[index], ...mapMessage(patch) };
+            messages.splice(index, 1, message);
+
+            renderer.setState(
+                channel,
+                {
+                    ...renderer.state,
+                    messages,
+                },
+                { type: "StayAtBottom" },
+            );
+        }
+    },
+    delete: async (renderer, id) => {
+        const channel = renderer.channel;
+        if (!channel) return;
+        if (renderer.state.type !== "RENDER") return;
+
+        let messages = [...renderer.state.messages];
+        let index = messages.findIndex((x) => x._id === id);
+
+        if (index > -1) {
+            messages.splice(index, 1);
+
+            renderer.setState(
+                channel,
+                {
+                    ...renderer.state,
+                    messages,
+                },
+                { type: "StayAtBottom" },
+            );
+        }
+    },
+    loadTop: async (renderer, generateScroll) => {
+        const channel = renderer.channel;
+        if (!channel) return;
+
+        const state = renderer.state;
+        if (state.type !== "RENDER") return;
+        if (state.atTop) return;
+
+        const { messages: data } =
+            await renderer.client!.channels.fetchMessagesWithUsers(
+                channel,
+                {
+                    before: state.messages[0]._id,
+                },
+                true,
+            );
+
+        if (data.length === 0) {
+            return renderer.setState(channel, {
+                ...state,
+                atTop: true,
+            });
+        }
+
+        data.reverse();
+        let messages = [...data.map((x) => mapMessage(x)), ...state.messages];
+
+        let atTop = false;
+        if (data.length < 50) {
+            atTop = true;
+        }
+
+        let atBottom = state.atBottom;
+        if (messages.length > 150) {
+            messages = messages.slice(0, 150);
+            atBottom = false;
+        }
+
+        renderer.setState(
+            channel,
+            { ...state, atTop, atBottom, messages },
+            generateScroll(messages[messages.length - 1]._id),
+        );
+    },
+    loadBottom: async (renderer, generateScroll) => {
+        const channel = renderer.channel;
+        if (!channel) return;
+
+        const state = renderer.state;
+        if (state.type !== "RENDER") return;
+        if (state.atBottom) return;
+
+        const { messages: data } =
+            await renderer.client!.channels.fetchMessagesWithUsers(
+                channel,
+                {
+                    after: state.messages[state.messages.length - 1]._id,
+                    sort: "Oldest",
+                },
+                true,
+            );
+
+        if (data.length === 0) {
+            return renderer.setState(channel, {
+                ...state,
+                atBottom: true,
+            });
+        }
+
+        let messages = [...state.messages, ...data.map((x) => mapMessage(x))];
+
+        let atBottom = false;
+        if (data.length < 50) {
+            atBottom = true;
+        }
+
+        let atTop = state.atTop;
+        if (messages.length > 150) {
+            messages = messages.slice(messages.length - 150);
+            atTop = false;
+        }
+
+        renderer.setState(
+            channel,
+            { ...state, atTop, atBottom, messages },
+            generateScroll(messages[0]._id),
+        );
+    },
 };
diff --git a/src/lib/renderer/types.ts b/src/lib/renderer/types.ts
index 29bb682..e066925 100644
--- a/src/lib/renderer/types.ts
+++ b/src/lib/renderer/types.ts
@@ -5,44 +5,44 @@ import { MessageObject } from "../../context/revoltjs/util";
 import { SingletonRenderer } from "./Singleton";
 
 export type ScrollState =
-	| { type: "Free" }
-	| { type: "Bottom"; scrollingUntil?: number }
-	| { type: "ScrollToBottom" | "StayAtBottom"; smooth?: boolean }
-	| { type: "OffsetTop"; previousHeight: number }
-	| { type: "ScrollTop"; y: number };
+    | { type: "Free" }
+    | { type: "Bottom"; scrollingUntil?: number }
+    | { type: "ScrollToBottom" | "StayAtBottom"; smooth?: boolean }
+    | { type: "OffsetTop"; previousHeight: number }
+    | { type: "ScrollTop"; y: number };
 
 export type RenderState =
-	| {
-			type: "LOADING" | "WAITING_FOR_NETWORK" | "EMPTY";
-	  }
-	| {
-			type: "RENDER";
-			atTop: boolean;
-			atBottom: boolean;
-			messages: MessageObject[];
-	  };
+    | {
+          type: "LOADING" | "WAITING_FOR_NETWORK" | "EMPTY";
+      }
+    | {
+          type: "RENDER";
+          atTop: boolean;
+          atBottom: boolean;
+          messages: MessageObject[];
+      };
 
 export interface RendererRoutines {
-	init: (
-		renderer: SingletonRenderer,
-		id: string,
-		smooth?: boolean,
-	) => Promise<void>;
+    init: (
+        renderer: SingletonRenderer,
+        id: string,
+        smooth?: boolean,
+    ) => Promise<void>;
 
-	receive: (renderer: SingletonRenderer, message: Message) => Promise<void>;
-	edit: (
-		renderer: SingletonRenderer,
-		id: string,
-		partial: Partial<Message>,
-	) => Promise<void>;
-	delete: (renderer: SingletonRenderer, id: string) => Promise<void>;
+    receive: (renderer: SingletonRenderer, message: Message) => Promise<void>;
+    edit: (
+        renderer: SingletonRenderer,
+        id: string,
+        partial: Partial<Message>,
+    ) => Promise<void>;
+    delete: (renderer: SingletonRenderer, id: string) => Promise<void>;
 
-	loadTop: (
-		renderer: SingletonRenderer,
-		generateScroll: (end: string) => ScrollState,
-	) => Promise<void>;
-	loadBottom: (
-		renderer: SingletonRenderer,
-		generateScroll: (start: string) => ScrollState,
-	) => Promise<void>;
+    loadTop: (
+        renderer: SingletonRenderer,
+        generateScroll: (end: string) => ScrollState,
+    ) => Promise<void>;
+    loadBottom: (
+        renderer: SingletonRenderer,
+        generateScroll: (start: string) => ScrollState,
+    ) => Promise<void>;
 }
diff --git a/src/lib/stopPropagation.ts b/src/lib/stopPropagation.ts
index 0a74bae..05043d2 100644
--- a/src/lib/stopPropagation.ts
+++ b/src/lib/stopPropagation.ts
@@ -1,8 +1,8 @@
 export const stopPropagation = (
-	ev: JSX.TargetedMouseEvent<HTMLDivElement>,
-	_consume?: any,
+    ev: JSX.TargetedMouseEvent<HTMLDivElement>,
+    _consume?: any,
 ) => {
-	ev.preventDefault();
-	ev.stopPropagation();
-	return true;
+    ev.preventDefault();
+    ev.stopPropagation();
+    return true;
 };
diff --git a/src/lib/vortex/Signaling.ts b/src/lib/vortex/Signaling.ts
index 5def904..916f965 100644
--- a/src/lib/vortex/Signaling.ts
+++ b/src/lib/vortex/Signaling.ts
@@ -1,189 +1,189 @@
 import EventEmitter from "eventemitter3";
 
 import {
-	RtpCapabilities,
-	RtpParameters,
+    RtpCapabilities,
+    RtpParameters,
 } from "mediasoup-client/lib/RtpParameters";
 import { DtlsParameters } from "mediasoup-client/lib/Transport";
 
 import {
-	AuthenticationResult,
-	Room,
-	TransportInitDataTuple,
-	WSCommandType,
-	WSErrorCode,
-	ProduceType,
-	ConsumerData,
+    AuthenticationResult,
+    Room,
+    TransportInitDataTuple,
+    WSCommandType,
+    WSErrorCode,
+    ProduceType,
+    ConsumerData,
 } from "./Types";
 
 interface SignalingEvents {
-	open: (event: Event) => void;
-	close: (event: CloseEvent) => void;
-	error: (event: Event) => void;
-	data: (data: any) => void;
+    open: (event: Event) => void;
+    close: (event: CloseEvent) => void;
+    error: (event: Event) => void;
+    data: (data: any) => void;
 }
 
 export default class Signaling extends EventEmitter<SignalingEvents> {
-	ws?: WebSocket;
-	index: number;
-	pending: Map<number, (data: unknown) => void>;
-
-	constructor() {
-		super();
-		this.index = 0;
-		this.pending = new Map();
-	}
-
-	connected(): boolean {
-		return (
-			this.ws !== undefined &&
-			this.ws.readyState !== WebSocket.CLOSING &&
-			this.ws.readyState !== WebSocket.CLOSED
-		);
-	}
-
-	connect(address: string): Promise<void> {
-		this.disconnect();
-		this.ws = new WebSocket(address);
-		this.ws.onopen = (e) => this.emit("open", e);
-		this.ws.onclose = (e) => this.emit("close", e);
-		this.ws.onerror = (e) => this.emit("error", e);
-		this.ws.onmessage = (e) => this.parseData(e);
-
-		let finished = false;
-		return new Promise((resolve, reject) => {
-			this.once("open", () => {
-				if (finished) return;
-				finished = true;
-				resolve();
-			});
-
-			this.once("error", () => {
-				if (finished) return;
-				finished = true;
-				reject();
-			});
-		});
-	}
-
-	disconnect() {
-		if (
-			this.ws !== undefined &&
-			this.ws.readyState !== WebSocket.CLOSED &&
-			this.ws.readyState !== WebSocket.CLOSING
-		)
-			this.ws.close(1000);
-	}
-
-	private parseData(event: MessageEvent) {
-		if (typeof event.data !== "string") return;
-		const json = JSON.parse(event.data);
-		const entry = this.pending.get(json.id);
-		if (entry === undefined) {
-			this.emit("data", json);
-			return;
-		}
-
-		entry(json);
-	}
-
-	sendRequest(type: string, data?: any): Promise<any> {
-		if (this.ws === undefined || this.ws.readyState !== WebSocket.OPEN)
-			return Promise.reject({ error: WSErrorCode.NotConnected });
-
-		const ws = this.ws;
-		return new Promise((resolve, reject) => {
-			if (this.index >= 2 ** 32) this.index = 0;
-			while (this.pending.has(this.index)) this.index++;
-			const onClose = (e: CloseEvent) => {
-				reject({
-					error: e.code,
-					message: e.reason,
-				});
-			};
-
-			const finishedFn = (data: any) => {
-				this.removeListener("close", onClose);
-				if (data.error)
-					reject({
-						error: data.error,
-						message: data.message,
-						data: data.data,
-					});
-				resolve(data.data);
-			};
-
-			this.pending.set(this.index, finishedFn);
-			this.once("close", onClose);
-			const json = {
-				id: this.index,
-				type: type,
-				data,
-			};
-			ws.send(JSON.stringify(json) + "\n");
-			this.index++;
-		});
-	}
-
-	authenticate(token: string, roomId: string): Promise<AuthenticationResult> {
-		return this.sendRequest(WSCommandType.Authenticate, { token, roomId });
-	}
-
-	async roomInfo(): Promise<Room> {
-		const room = await this.sendRequest(WSCommandType.RoomInfo);
-		return {
-			id: room.id,
-			videoAllowed: room.videoAllowed,
-			users: new Map(Object.entries(room.users)),
-		};
-	}
-
-	initializeTransports(
-		rtpCapabilities: RtpCapabilities,
-	): Promise<TransportInitDataTuple> {
-		return this.sendRequest(WSCommandType.InitializeTransports, {
-			mode: "SplitWebRTC",
-			rtpCapabilities,
-		});
-	}
-
-	connectTransport(
-		id: string,
-		dtlsParameters: DtlsParameters,
-	): Promise<void> {
-		return this.sendRequest(WSCommandType.ConnectTransport, {
-			id,
-			dtlsParameters,
-		});
-	}
-
-	async startProduce(
-		type: ProduceType,
-		rtpParameters: RtpParameters,
-	): Promise<string> {
-		let result = await this.sendRequest(WSCommandType.StartProduce, {
-			type,
-			rtpParameters,
-		});
-		return result.producerId;
-	}
-
-	stopProduce(type: ProduceType): Promise<void> {
-		return this.sendRequest(WSCommandType.StopProduce, { type });
-	}
-
-	startConsume(userId: string, type: ProduceType): Promise<ConsumerData> {
-		return this.sendRequest(WSCommandType.StartConsume, { type, userId });
-	}
-
-	stopConsume(consumerId: string): Promise<void> {
-		return this.sendRequest(WSCommandType.StopConsume, { id: consumerId });
-	}
-
-	setConsumerPause(consumerId: string, paused: boolean): Promise<void> {
-		return this.sendRequest(WSCommandType.SetConsumerPause, {
-			id: consumerId,
-			paused,
-		});
-	}
+    ws?: WebSocket;
+    index: number;
+    pending: Map<number, (data: unknown) => void>;
+
+    constructor() {
+        super();
+        this.index = 0;
+        this.pending = new Map();
+    }
+
+    connected(): boolean {
+        return (
+            this.ws !== undefined &&
+            this.ws.readyState !== WebSocket.CLOSING &&
+            this.ws.readyState !== WebSocket.CLOSED
+        );
+    }
+
+    connect(address: string): Promise<void> {
+        this.disconnect();
+        this.ws = new WebSocket(address);
+        this.ws.onopen = (e) => this.emit("open", e);
+        this.ws.onclose = (e) => this.emit("close", e);
+        this.ws.onerror = (e) => this.emit("error", e);
+        this.ws.onmessage = (e) => this.parseData(e);
+
+        let finished = false;
+        return new Promise((resolve, reject) => {
+            this.once("open", () => {
+                if (finished) return;
+                finished = true;
+                resolve();
+            });
+
+            this.once("error", () => {
+                if (finished) return;
+                finished = true;
+                reject();
+            });
+        });
+    }
+
+    disconnect() {
+        if (
+            this.ws !== undefined &&
+            this.ws.readyState !== WebSocket.CLOSED &&
+            this.ws.readyState !== WebSocket.CLOSING
+        )
+            this.ws.close(1000);
+    }
+
+    private parseData(event: MessageEvent) {
+        if (typeof event.data !== "string") return;
+        const json = JSON.parse(event.data);
+        const entry = this.pending.get(json.id);
+        if (entry === undefined) {
+            this.emit("data", json);
+            return;
+        }
+
+        entry(json);
+    }
+
+    sendRequest(type: string, data?: any): Promise<any> {
+        if (this.ws === undefined || this.ws.readyState !== WebSocket.OPEN)
+            return Promise.reject({ error: WSErrorCode.NotConnected });
+
+        const ws = this.ws;
+        return new Promise((resolve, reject) => {
+            if (this.index >= 2 ** 32) this.index = 0;
+            while (this.pending.has(this.index)) this.index++;
+            const onClose = (e: CloseEvent) => {
+                reject({
+                    error: e.code,
+                    message: e.reason,
+                });
+            };
+
+            const finishedFn = (data: any) => {
+                this.removeListener("close", onClose);
+                if (data.error)
+                    reject({
+                        error: data.error,
+                        message: data.message,
+                        data: data.data,
+                    });
+                resolve(data.data);
+            };
+
+            this.pending.set(this.index, finishedFn);
+            this.once("close", onClose);
+            const json = {
+                id: this.index,
+                type: type,
+                data,
+            };
+            ws.send(JSON.stringify(json) + "\n");
+            this.index++;
+        });
+    }
+
+    authenticate(token: string, roomId: string): Promise<AuthenticationResult> {
+        return this.sendRequest(WSCommandType.Authenticate, { token, roomId });
+    }
+
+    async roomInfo(): Promise<Room> {
+        const room = await this.sendRequest(WSCommandType.RoomInfo);
+        return {
+            id: room.id,
+            videoAllowed: room.videoAllowed,
+            users: new Map(Object.entries(room.users)),
+        };
+    }
+
+    initializeTransports(
+        rtpCapabilities: RtpCapabilities,
+    ): Promise<TransportInitDataTuple> {
+        return this.sendRequest(WSCommandType.InitializeTransports, {
+            mode: "SplitWebRTC",
+            rtpCapabilities,
+        });
+    }
+
+    connectTransport(
+        id: string,
+        dtlsParameters: DtlsParameters,
+    ): Promise<void> {
+        return this.sendRequest(WSCommandType.ConnectTransport, {
+            id,
+            dtlsParameters,
+        });
+    }
+
+    async startProduce(
+        type: ProduceType,
+        rtpParameters: RtpParameters,
+    ): Promise<string> {
+        let result = await this.sendRequest(WSCommandType.StartProduce, {
+            type,
+            rtpParameters,
+        });
+        return result.producerId;
+    }
+
+    stopProduce(type: ProduceType): Promise<void> {
+        return this.sendRequest(WSCommandType.StopProduce, { type });
+    }
+
+    startConsume(userId: string, type: ProduceType): Promise<ConsumerData> {
+        return this.sendRequest(WSCommandType.StartConsume, { type, userId });
+    }
+
+    stopConsume(consumerId: string): Promise<void> {
+        return this.sendRequest(WSCommandType.StopConsume, { id: consumerId });
+    }
+
+    setConsumerPause(consumerId: string, paused: boolean): Promise<void> {
+        return this.sendRequest(WSCommandType.SetConsumerPause, {
+            id: consumerId,
+            paused,
+        });
+    }
 }
diff --git a/src/lib/vortex/Types.ts b/src/lib/vortex/Types.ts
index 5ab09a4..7cee61c 100644
--- a/src/lib/vortex/Types.ts
+++ b/src/lib/vortex/Types.ts
@@ -1,111 +1,111 @@
 import { Consumer } from "mediasoup-client/lib/Consumer";
 import {
-	MediaKind,
-	RtpCapabilities,
-	RtpParameters,
+    MediaKind,
+    RtpCapabilities,
+    RtpParameters,
 } from "mediasoup-client/lib/RtpParameters";
 import { SctpParameters } from "mediasoup-client/lib/SctpParameters";
 import {
-	DtlsParameters,
-	IceCandidate,
-	IceParameters,
+    DtlsParameters,
+    IceCandidate,
+    IceParameters,
 } from "mediasoup-client/lib/Transport";
 
 export enum WSEventType {
-	UserJoined = "UserJoined",
-	UserLeft = "UserLeft",
+    UserJoined = "UserJoined",
+    UserLeft = "UserLeft",
 
-	UserStartProduce = "UserStartProduce",
-	UserStopProduce = "UserStopProduce",
+    UserStartProduce = "UserStartProduce",
+    UserStopProduce = "UserStopProduce",
 }
 
 export enum WSCommandType {
-	Authenticate = "Authenticate",
-	RoomInfo = "RoomInfo",
+    Authenticate = "Authenticate",
+    RoomInfo = "RoomInfo",
 
-	InitializeTransports = "InitializeTransports",
-	ConnectTransport = "ConnectTransport",
+    InitializeTransports = "InitializeTransports",
+    ConnectTransport = "ConnectTransport",
 
-	StartProduce = "StartProduce",
-	StopProduce = "StopProduce",
+    StartProduce = "StartProduce",
+    StopProduce = "StopProduce",
 
-	StartConsume = "StartConsume",
-	StopConsume = "StopConsume",
-	SetConsumerPause = "SetConsumerPause",
+    StartConsume = "StartConsume",
+    StopConsume = "StopConsume",
+    SetConsumerPause = "SetConsumerPause",
 }
 
 export enum WSErrorCode {
-	NotConnected = 0,
-	NotFound = 404,
+    NotConnected = 0,
+    NotFound = 404,
 
-	TransportConnectionFailure = 601,
+    TransportConnectionFailure = 601,
 
-	ProducerFailure = 611,
-	ProducerNotFound = 614,
+    ProducerFailure = 611,
+    ProducerNotFound = 614,
 
-	ConsumerFailure = 621,
-	ConsumerNotFound = 624,
+    ConsumerFailure = 621,
+    ConsumerNotFound = 624,
 }
 
 export enum WSCloseCode {
-	// Sent when the received data is not a string, or is unparseable
-	InvalidData = 1003,
-	Unauthorized = 4001,
-	RoomClosed = 4004,
-	// Sent when a client tries to send an opcode in the wrong state
-	InvalidState = 1002,
-	ServerError = 1011,
+    // Sent when the received data is not a string, or is unparseable
+    InvalidData = 1003,
+    Unauthorized = 4001,
+    RoomClosed = 4004,
+    // Sent when a client tries to send an opcode in the wrong state
+    InvalidState = 1002,
+    ServerError = 1011,
 }
 
 export interface VoiceError {
-	error: WSErrorCode | WSCloseCode;
-	message: string;
+    error: WSErrorCode | WSCloseCode;
+    message: string;
 }
 
 export type ProduceType = "audio"; //| "video" | "saudio" | "svideo";
 
 export interface AuthenticationResult {
-	userId: string;
-	roomId: string;
-	rtpCapabilities: RtpCapabilities;
+    userId: string;
+    roomId: string;
+    rtpCapabilities: RtpCapabilities;
 }
 
 export interface Room {
-	id: string;
-	videoAllowed: boolean;
-	users: Map<string, VoiceUser>;
+    id: string;
+    videoAllowed: boolean;
+    users: Map<string, VoiceUser>;
 }
 
 export interface VoiceUser {
-	audio?: boolean;
-	//video?: boolean,
-	//saudio?: boolean,
-	//svideo?: boolean,
+    audio?: boolean;
+    //video?: boolean,
+    //saudio?: boolean,
+    //svideo?: boolean,
 }
 
 export interface ConsumerList {
-	audio?: Consumer;
-	//video?: Consumer,
-	//saudio?: Consumer,
-	//svideo?: Consumer,
+    audio?: Consumer;
+    //video?: Consumer,
+    //saudio?: Consumer,
+    //svideo?: Consumer,
 }
 
 export interface TransportInitData {
-	id: string;
-	iceParameters: IceParameters;
-	iceCandidates: IceCandidate[];
-	dtlsParameters: DtlsParameters;
-	sctpParameters: SctpParameters | undefined;
+    id: string;
+    iceParameters: IceParameters;
+    iceCandidates: IceCandidate[];
+    dtlsParameters: DtlsParameters;
+    sctpParameters: SctpParameters | undefined;
 }
 
 export interface TransportInitDataTuple {
-	sendTransport: TransportInitData;
-	recvTransport: TransportInitData;
+    sendTransport: TransportInitData;
+    recvTransport: TransportInitData;
 }
 
 export interface ConsumerData {
-	id: string;
-	producerId: string;
-	kind: MediaKind;
-	rtpParameters: RtpParameters;
+    id: string;
+    producerId: string;
+    kind: MediaKind;
+    rtpParameters: RtpParameters;
 }
diff --git a/src/lib/vortex/VoiceClient.ts b/src/lib/vortex/VoiceClient.ts
index 9ebeab6..f0fede6 100644
--- a/src/lib/vortex/VoiceClient.ts
+++ b/src/lib/vortex/VoiceClient.ts
@@ -2,330 +2,330 @@ import EventEmitter from "eventemitter3";
 import * as mediasoupClient from "mediasoup-client";
 
 import {
-	Device,
-	Producer,
-	Transport,
-	UnsupportedError,
+    Device,
+    Producer,
+    Transport,
+    UnsupportedError,
 } from "mediasoup-client/lib/types";
 
 import Signaling from "./Signaling";
 import {
-	ProduceType,
-	WSEventType,
-	VoiceError,
-	VoiceUser,
-	ConsumerList,
-	WSErrorCode,
+    ProduceType,
+    WSEventType,
+    VoiceError,
+    VoiceUser,
+    ConsumerList,
+    WSErrorCode,
 } from "./Types";
 
 interface VoiceEvents {
-	ready: () => void;
-	error: (error: Error) => void;
-	close: (error?: VoiceError) => void;
+    ready: () => void;
+    error: (error: Error) => void;
+    close: (error?: VoiceError) => void;
 
-	startProduce: (type: ProduceType) => void;
-	stopProduce: (type: ProduceType) => void;
+    startProduce: (type: ProduceType) => void;
+    stopProduce: (type: ProduceType) => void;
 
-	userJoined: (userId: string) => void;
-	userLeft: (userId: string) => void;
+    userJoined: (userId: string) => void;
+    userLeft: (userId: string) => void;
 
-	userStartProduce: (userId: string, type: ProduceType) => void;
-	userStopProduce: (userId: string, type: ProduceType) => void;
+    userStartProduce: (userId: string, type: ProduceType) => void;
+    userStopProduce: (userId: string, type: ProduceType) => void;
 }
 
 export default class VoiceClient extends EventEmitter<VoiceEvents> {
-	private _supported: boolean;
-
-	device?: Device;
-	signaling: Signaling;
-
-	sendTransport?: Transport;
-	recvTransport?: Transport;
-
-	userId?: string;
-	roomId?: string;
-	participants: Map<string, VoiceUser>;
-	consumers: Map<string, ConsumerList>;
-
-	audioProducer?: Producer;
-	constructor() {
-		super();
-		this._supported = mediasoupClient.detectDevice() !== undefined;
-		this.signaling = new Signaling();
-
-		this.participants = new Map();
-		this.consumers = new Map();
-
-		this.signaling.on(
-			"data",
-			(json) => {
-				const data = json.data;
-				switch (json.type) {
-					case WSEventType.UserJoined: {
-						this.participants.set(data.id, {});
-						this.emit("userJoined", data.id);
-						break;
-					}
-					case WSEventType.UserLeft: {
-						this.participants.delete(data.id);
-						this.emit("userLeft", data.id);
-
-						if (this.recvTransport) this.stopConsume(data.id);
-						break;
-					}
-					case WSEventType.UserStartProduce: {
-						const user = this.participants.get(data.id);
-						if (user === undefined) return;
-						switch (data.type) {
-							case "audio":
-								user.audio = true;
-								break;
-							default:
-								throw new Error(
-									`Invalid produce type ${data.type}`,
-								);
-						}
-
-						if (this.recvTransport)
-							this.startConsume(data.id, data.type);
-						this.emit("userStartProduce", data.id, data.type);
-						break;
-					}
-					case WSEventType.UserStopProduce: {
-						const user = this.participants.get(data.id);
-						if (user === undefined) return;
-						switch (data.type) {
-							case "audio":
-								user.audio = false;
-								break;
-							default:
-								throw new Error(
-									`Invalid produce type ${data.type}`,
-								);
-						}
-
-						if (this.recvTransport)
-							this.stopConsume(data.id, data.type);
-						this.emit("userStopProduce", data.id, data.type);
-						break;
-					}
-				}
-			},
-			this,
-		);
-
-		this.signaling.on(
-			"error",
-			(error) => {
-				this.emit("error", new Error("Signaling error"));
-			},
-			this,
-		);
-
-		this.signaling.on(
-			"close",
-			(error) => {
-				this.disconnect(
-					{
-						error: error.code,
-						message: error.reason,
-					},
-					true,
-				);
-			},
-			this,
-		);
-	}
-
-	supported() {
-		return this._supported;
-	}
-	throwIfUnsupported() {
-		if (!this._supported) throw new UnsupportedError("RTC not supported");
-	}
-
-	connect(address: string, roomId: string) {
-		this.throwIfUnsupported();
-		this.device = new Device();
-		this.roomId = roomId;
-		return this.signaling.connect(address);
-	}
-
-	disconnect(error?: VoiceError, ignoreDisconnected?: boolean) {
-		if (!this.signaling.connected() && !ignoreDisconnected) return;
-		this.signaling.disconnect();
-		this.participants = new Map();
-		this.consumers = new Map();
-		this.userId = undefined;
-		this.roomId = undefined;
-
-		this.audioProducer = undefined;
-
-		if (this.sendTransport) this.sendTransport.close();
-		if (this.recvTransport) this.recvTransport.close();
-		this.sendTransport = undefined;
-		this.recvTransport = undefined;
-
-		this.emit("close", error);
-	}
-
-	async authenticate(token: string) {
-		this.throwIfUnsupported();
-		if (this.device === undefined || this.roomId === undefined)
-			throw new ReferenceError("Voice Client is in an invalid state");
-		const result = await this.signaling.authenticate(token, this.roomId);
-		let [room] = await Promise.all([
-			this.signaling.roomInfo(),
-			this.device.load({ routerRtpCapabilities: result.rtpCapabilities }),
-		]);
-
-		this.userId = result.userId;
-		this.participants = room.users;
-	}
-
-	async initializeTransports() {
-		this.throwIfUnsupported();
-		if (this.device === undefined)
-			throw new ReferenceError("Voice Client is in an invalid state");
-		const initData = await this.signaling.initializeTransports(
-			this.device.rtpCapabilities,
-		);
-
-		this.sendTransport = this.device.createSendTransport(
-			initData.sendTransport,
-		);
-		this.recvTransport = this.device.createRecvTransport(
-			initData.recvTransport,
-		);
-
-		const connectTransport = (transport: Transport) => {
-			transport.on("connect", ({ dtlsParameters }, callback, errback) => {
-				this.signaling
-					.connectTransport(transport.id, dtlsParameters)
-					.then(callback)
-					.catch(errback);
-			});
-		};
-
-		connectTransport(this.sendTransport);
-		connectTransport(this.recvTransport);
-
-		this.sendTransport.on("produce", (parameters, callback, errback) => {
-			const type = parameters.appData.type;
-			if (
-				parameters.kind === "audio" &&
-				type !== "audio" &&
-				type !== "saudio"
-			)
-				return errback();
-			if (
-				parameters.kind === "video" &&
-				type !== "video" &&
-				type !== "svideo"
-			)
-				return errback();
-			this.signaling
-				.startProduce(type, parameters.rtpParameters)
-				.then((id) => callback({ id }))
-				.catch(errback);
-		});
-
-		this.emit("ready");
-		for (let user of this.participants) {
-			if (user[1].audio && user[0] !== this.userId)
-				this.startConsume(user[0], "audio");
-		}
-	}
-
-	private async startConsume(userId: string, type: ProduceType) {
-		if (this.recvTransport === undefined)
-			throw new Error("Receive transport undefined");
-		const consumers = this.consumers.get(userId) || {};
-		const consumerParams = await this.signaling.startConsume(userId, type);
-		const consumer = await this.recvTransport.consume(consumerParams);
-		switch (type) {
-			case "audio":
-				consumers.audio = consumer;
-		}
-
-		const mediaStream = new MediaStream([consumer.track]);
-		const audio = new Audio();
-		audio.srcObject = mediaStream;
-		await this.signaling.setConsumerPause(consumer.id, false);
-		audio.play();
-		this.consumers.set(userId, consumers);
-	}
-
-	private async stopConsume(userId: string, type?: ProduceType) {
-		const consumers = this.consumers.get(userId);
-		if (consumers === undefined) return;
-		if (type === undefined) {
-			if (consumers.audio !== undefined) consumers.audio.close();
-			this.consumers.delete(userId);
-		} else {
-			switch (type) {
-				case "audio": {
-					if (consumers.audio !== undefined) {
-						consumers.audio.close();
-						this.signaling.stopConsume(consumers.audio.id);
-					}
-					consumers.audio = undefined;
-					break;
-				}
-			}
-
-			this.consumers.set(userId, consumers);
-		}
-	}
-
-	async startProduce(track: MediaStreamTrack, type: ProduceType) {
-		if (this.sendTransport === undefined)
-			throw new Error("Send transport undefined");
-		const producer = await this.sendTransport.produce({
-			track,
-			appData: { type },
-		});
-
-		switch (type) {
-			case "audio":
-				this.audioProducer = producer;
-				break;
-		}
-
-		const participant = this.participants.get(this.userId || "");
-		if (participant !== undefined) {
-			participant[type] = true;
-			this.participants.set(this.userId || "", participant);
-		}
-
-		this.emit("startProduce", type);
-	}
-
-	async stopProduce(type: ProduceType) {
-		let producer;
-		switch (type) {
-			case "audio":
-				producer = this.audioProducer;
-				this.audioProducer = undefined;
-				break;
-		}
-
-		if (producer !== undefined) {
-			producer.close();
-			this.emit("stopProduce", type);
-		}
-
-		const participant = this.participants.get(this.userId || "");
-		if (participant !== undefined) {
-			participant[type] = false;
-			this.participants.set(this.userId || "", participant);
-		}
-
-		try {
-			await this.signaling.stopProduce(type);
-		} catch (error) {
-			if (error.error === WSErrorCode.ProducerNotFound) return;
-			else throw error;
-		}
-	}
+    private _supported: boolean;
+
+    device?: Device;
+    signaling: Signaling;
+
+    sendTransport?: Transport;
+    recvTransport?: Transport;
+
+    userId?: string;
+    roomId?: string;
+    participants: Map<string, VoiceUser>;
+    consumers: Map<string, ConsumerList>;
+
+    audioProducer?: Producer;
+    constructor() {
+        super();
+        this._supported = mediasoupClient.detectDevice() !== undefined;
+        this.signaling = new Signaling();
+
+        this.participants = new Map();
+        this.consumers = new Map();
+
+        this.signaling.on(
+            "data",
+            (json) => {
+                const data = json.data;
+                switch (json.type) {
+                    case WSEventType.UserJoined: {
+                        this.participants.set(data.id, {});
+                        this.emit("userJoined", data.id);
+                        break;
+                    }
+                    case WSEventType.UserLeft: {
+                        this.participants.delete(data.id);
+                        this.emit("userLeft", data.id);
+
+                        if (this.recvTransport) this.stopConsume(data.id);
+                        break;
+                    }
+                    case WSEventType.UserStartProduce: {
+                        const user = this.participants.get(data.id);
+                        if (user === undefined) return;
+                        switch (data.type) {
+                            case "audio":
+                                user.audio = true;
+                                break;
+                            default:
+                                throw new Error(
+                                    `Invalid produce type ${data.type}`,
+                                );
+                        }
+
+                        if (this.recvTransport)
+                            this.startConsume(data.id, data.type);
+                        this.emit("userStartProduce", data.id, data.type);
+                        break;
+                    }
+                    case WSEventType.UserStopProduce: {
+                        const user = this.participants.get(data.id);
+                        if (user === undefined) return;
+                        switch (data.type) {
+                            case "audio":
+                                user.audio = false;
+                                break;
+                            default:
+                                throw new Error(
+                                    `Invalid produce type ${data.type}`,
+                                );
+                        }
+
+                        if (this.recvTransport)
+                            this.stopConsume(data.id, data.type);
+                        this.emit("userStopProduce", data.id, data.type);
+                        break;
+                    }
+                }
+            },
+            this,
+        );
+
+        this.signaling.on(
+            "error",
+            (error) => {
+                this.emit("error", new Error("Signaling error"));
+            },
+            this,
+        );
+
+        this.signaling.on(
+            "close",
+            (error) => {
+                this.disconnect(
+                    {
+                        error: error.code,
+                        message: error.reason,
+                    },
+                    true,
+                );
+            },
+            this,
+        );
+    }
+
+    supported() {
+        return this._supported;
+    }
+    throwIfUnsupported() {
+        if (!this._supported) throw new UnsupportedError("RTC not supported");
+    }
+
+    connect(address: string, roomId: string) {
+        this.throwIfUnsupported();
+        this.device = new Device();
+        this.roomId = roomId;
+        return this.signaling.connect(address);
+    }
+
+    disconnect(error?: VoiceError, ignoreDisconnected?: boolean) {
+        if (!this.signaling.connected() && !ignoreDisconnected) return;
+        this.signaling.disconnect();
+        this.participants = new Map();
+        this.consumers = new Map();
+        this.userId = undefined;
+        this.roomId = undefined;
+
+        this.audioProducer = undefined;
+
+        if (this.sendTransport) this.sendTransport.close();
+        if (this.recvTransport) this.recvTransport.close();
+        this.sendTransport = undefined;
+        this.recvTransport = undefined;
+
+        this.emit("close", error);
+    }
+
+    async authenticate(token: string) {
+        this.throwIfUnsupported();
+        if (this.device === undefined || this.roomId === undefined)
+            throw new ReferenceError("Voice Client is in an invalid state");
+        const result = await this.signaling.authenticate(token, this.roomId);
+        let [room] = await Promise.all([
+            this.signaling.roomInfo(),
+            this.device.load({ routerRtpCapabilities: result.rtpCapabilities }),
+        ]);
+
+        this.userId = result.userId;
+        this.participants = room.users;
+    }
+
+    async initializeTransports() {
+        this.throwIfUnsupported();
+        if (this.device === undefined)
+            throw new ReferenceError("Voice Client is in an invalid state");
+        const initData = await this.signaling.initializeTransports(
+            this.device.rtpCapabilities,
+        );
+
+        this.sendTransport = this.device.createSendTransport(
+            initData.sendTransport,
+        );
+        this.recvTransport = this.device.createRecvTransport(
+            initData.recvTransport,
+        );
+
+        const connectTransport = (transport: Transport) => {
+            transport.on("connect", ({ dtlsParameters }, callback, errback) => {
+                this.signaling
+                    .connectTransport(transport.id, dtlsParameters)
+                    .then(callback)
+                    .catch(errback);
+            });
+        };
+
+        connectTransport(this.sendTransport);
+        connectTransport(this.recvTransport);
+
+        this.sendTransport.on("produce", (parameters, callback, errback) => {
+            const type = parameters.appData.type;
+            if (
+                parameters.kind === "audio" &&
+                type !== "audio" &&
+                type !== "saudio"
+            )
+                return errback();
+            if (
+                parameters.kind === "video" &&
+                type !== "video" &&
+                type !== "svideo"
+            )
+                return errback();
+            this.signaling
+                .startProduce(type, parameters.rtpParameters)
+                .then((id) => callback({ id }))
+                .catch(errback);
+        });
+
+        this.emit("ready");
+        for (let user of this.participants) {
+            if (user[1].audio && user[0] !== this.userId)
+                this.startConsume(user[0], "audio");
+        }
+    }
+
+    private async startConsume(userId: string, type: ProduceType) {
+        if (this.recvTransport === undefined)
+            throw new Error("Receive transport undefined");
+        const consumers = this.consumers.get(userId) || {};
+        const consumerParams = await this.signaling.startConsume(userId, type);
+        const consumer = await this.recvTransport.consume(consumerParams);
+        switch (type) {
+            case "audio":
+                consumers.audio = consumer;
+        }
+
+        const mediaStream = new MediaStream([consumer.track]);
+        const audio = new Audio();
+        audio.srcObject = mediaStream;
+        await this.signaling.setConsumerPause(consumer.id, false);
+        audio.play();
+        this.consumers.set(userId, consumers);
+    }
+
+    private async stopConsume(userId: string, type?: ProduceType) {
+        const consumers = this.consumers.get(userId);
+        if (consumers === undefined) return;
+        if (type === undefined) {
+            if (consumers.audio !== undefined) consumers.audio.close();
+            this.consumers.delete(userId);
+        } else {
+            switch (type) {
+                case "audio": {
+                    if (consumers.audio !== undefined) {
+                        consumers.audio.close();
+                        this.signaling.stopConsume(consumers.audio.id);
+                    }
+                    consumers.audio = undefined;
+                    break;
+                }
+            }
+
+            this.consumers.set(userId, consumers);
+        }
+    }
+
+    async startProduce(track: MediaStreamTrack, type: ProduceType) {
+        if (this.sendTransport === undefined)
+            throw new Error("Send transport undefined");
+        const producer = await this.sendTransport.produce({
+            track,
+            appData: { type },
+        });
+
+        switch (type) {
+            case "audio":
+                this.audioProducer = producer;
+                break;
+        }
+
+        const participant = this.participants.get(this.userId || "");
+        if (participant !== undefined) {
+            participant[type] = true;
+            this.participants.set(this.userId || "", participant);
+        }
+
+        this.emit("startProduce", type);
+    }
+
+    async stopProduce(type: ProduceType) {
+        let producer;
+        switch (type) {
+            case "audio":
+                producer = this.audioProducer;
+                this.audioProducer = undefined;
+                break;
+        }
+
+        if (producer !== undefined) {
+            producer.close();
+            this.emit("stopProduce", type);
+        }
+
+        const participant = this.participants.get(this.userId || "");
+        if (participant !== undefined) {
+            participant[type] = false;
+            this.participants.set(this.userId || "", participant);
+        }
+
+        try {
+            await this.signaling.stopProduce(type);
+        } catch (error) {
+            if (error.error === WSErrorCode.ProducerNotFound) return;
+            else throw error;
+        }
+    }
 }
diff --git a/src/lib/windowSize.ts b/src/lib/windowSize.ts
index 770dcd6..842144d 100644
--- a/src/lib/windowSize.ts
+++ b/src/lib/windowSize.ts
@@ -1,24 +1,24 @@
 import { useEffect, useState } from "preact/hooks";
 
 export function useWindowSize() {
-	const [windowSize, setWindowSize] = useState({
-		width: window.innerWidth,
-		height: window.innerHeight,
-	});
+    const [windowSize, setWindowSize] = useState({
+        width: window.innerWidth,
+        height: window.innerHeight,
+    });
 
-	useEffect(() => {
-		function handleResize() {
-			setWindowSize({
-				width: window.innerWidth,
-				height: window.innerHeight,
-			});
-		}
+    useEffect(() => {
+        function handleResize() {
+            setWindowSize({
+                width: window.innerWidth,
+                height: window.innerHeight,
+            });
+        }
 
-		window.addEventListener("resize", handleResize);
-		handleResize();
+        window.addEventListener("resize", handleResize);
+        handleResize();
 
-		return () => window.removeEventListener("resize", handleResize);
-	}, []);
+        return () => window.removeEventListener("resize", handleResize);
+    }, []);
 
-	return windowSize;
+    return windowSize;
 }
diff --git a/src/main.tsx b/src/main.tsx
index c842979..2808139 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -8,13 +8,13 @@ import { internalEmit } from "./lib/eventEmitter";
 import { App } from "./pages/app";
 
 export const updateSW = registerSW({
-	onNeedRefresh() {
-		internalEmit("PWA", "update");
-	},
-	onOfflineReady() {
-		console.info("Ready to work offline.");
-		// show a ready to work offline to user
-	},
+    onNeedRefresh() {
+        internalEmit("PWA", "update");
+    },
+    onOfflineReady() {
+        console.info("Ready to work offline.");
+        // show a ready to work offline to user
+    },
 });
 
 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
diff --git a/src/pages/Open.tsx b/src/pages/Open.tsx
index 32a471c..6a36e24 100644
--- a/src/pages/Open.tsx
+++ b/src/pages/Open.tsx
@@ -5,79 +5,79 @@ import { useContext, useEffect } from "preact/hooks";
 
 import { useIntermediate } from "../context/intermediate/Intermediate";
 import {
-	AppContext,
-	ClientStatus,
-	StatusContext,
+    AppContext,
+    ClientStatus,
+    StatusContext,
 } from "../context/revoltjs/RevoltClient";
 import {
-	useChannels,
-	useForceUpdate,
-	useUser,
+    useChannels,
+    useForceUpdate,
+    useUser,
 } from "../context/revoltjs/hooks";
 
 import Header from "../components/ui/Header";
 
 export default function Open() {
-	const history = useHistory();
-	const client = useContext(AppContext);
-	const status = useContext(StatusContext);
-	const { id } = useParams<{ id: string }>();
-	const { openScreen } = useIntermediate();
+    const history = useHistory();
+    const client = useContext(AppContext);
+    const status = useContext(StatusContext);
+    const { id } = useParams<{ id: string }>();
+    const { openScreen } = useIntermediate();
 
-	if (status !== ClientStatus.ONLINE) {
-		return (
-			<Header placement="primary">
-				<Text id="general.loading" />
-			</Header>
-		);
-	}
+    if (status !== ClientStatus.ONLINE) {
+        return (
+            <Header placement="primary">
+                <Text id="general.loading" />
+            </Header>
+        );
+    }
 
-	const ctx = useForceUpdate();
-	const channels = useChannels(undefined, ctx);
-	const user = useUser(id, ctx);
+    const ctx = useForceUpdate();
+    const channels = useChannels(undefined, ctx);
+    const user = useUser(id, ctx);
 
-	useEffect(() => {
-		if (id === "saved") {
-			for (const channel of channels) {
-				if (channel?.channel_type === "SavedMessages") {
-					history.push(`/channel/${channel._id}`);
-					return;
-				}
-			}
+    useEffect(() => {
+        if (id === "saved") {
+            for (const channel of channels) {
+                if (channel?.channel_type === "SavedMessages") {
+                    history.push(`/channel/${channel._id}`);
+                    return;
+                }
+            }
 
-			client.users
-				.openDM(client.user?._id as string)
-				.then((channel) => history.push(`/channel/${channel?._id}`))
-				.catch((error) => openScreen({ id: "error", error }));
+            client.users
+                .openDM(client.user?._id as string)
+                .then((channel) => history.push(`/channel/${channel?._id}`))
+                .catch((error) => openScreen({ id: "error", error }));
 
-			return;
-		}
+            return;
+        }
 
-		if (user) {
-			const channel: string | undefined = channels.find(
-				(channel) =>
-					channel?.channel_type === "DirectMessage" &&
-					channel.recipients.includes(id),
-			)?._id;
+        if (user) {
+            const channel: string | undefined = channels.find(
+                (channel) =>
+                    channel?.channel_type === "DirectMessage" &&
+                    channel.recipients.includes(id),
+            )?._id;
 
-			if (channel) {
-				history.push(`/channel/${channel}`);
-			} else {
-				client.users
-					.openDM(id)
-					.then((channel) => history.push(`/channel/${channel?._id}`))
-					.catch((error) => openScreen({ id: "error", error }));
-			}
+            if (channel) {
+                history.push(`/channel/${channel}`);
+            } else {
+                client.users
+                    .openDM(id)
+                    .then((channel) => history.push(`/channel/${channel?._id}`))
+                    .catch((error) => openScreen({ id: "error", error }));
+            }
 
-			return;
-		}
+            return;
+        }
 
-		history.push("/");
-	}, []);
+        history.push("/");
+    }, []);
 
-	return (
-		<Header placement="primary">
-			<Text id="general.loading" />
-		</Header>
-	);
+    return (
+        <Header placement="primary">
+            <Text id="general.loading" />
+        </Header>
+    );
 }
diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx
index da941de..d5137d8 100644
--- a/src/pages/RevoltApp.tsx
+++ b/src/pages/RevoltApp.tsx
@@ -24,97 +24,97 @@ import ServerSettings from "./settings/ServerSettings";
 import Settings from "./settings/Settings";
 
 const Routes = styled.div`
-	min-width: 0;
-	display: flex;
-	overflow: hidden;
-	flex-direction: column;
-	background: var(--primary-background);
+    min-width: 0;
+    display: flex;
+    overflow: hidden;
+    flex-direction: column;
+    background: var(--primary-background);
 `;
 
 export default function App() {
-	const path = useLocation().pathname;
-	const fixedBottomNav =
-		path === "/" || path === "/settings" || path.startsWith("/friends");
-	const inSettings = path.includes("/settings");
-	const inChannel = path.includes("/channel");
-	const inSpecial =
-		(path.startsWith("/friends") && isTouchscreenDevice) ||
-		path.startsWith("/invite") ||
-		path.startsWith("/settings");
+    const path = useLocation().pathname;
+    const fixedBottomNav =
+        path === "/" || path === "/settings" || path.startsWith("/friends");
+    const inSettings = path.includes("/settings");
+    const inChannel = path.includes("/channel");
+    const inSpecial =
+        (path.startsWith("/friends") && isTouchscreenDevice) ||
+        path.startsWith("/invite") ||
+        path.startsWith("/settings");
 
-	return (
-		<OverlappingPanels
-			width="100vw"
-			height="var(--app-height)"
-			leftPanel={
-				inSpecial
-					? undefined
-					: { width: 292, component: <LeftSidebar /> }
-			}
-			rightPanel={
-				!inSettings && inChannel
-					? { width: 240, component: <RightSidebar /> }
-					: undefined
-			}
-			bottomNav={{
-				component: <BottomNavigation />,
-				showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left,
-				height: 50,
-			}}
-			docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
-			<Routes>
-				<Switch>
-					<Route
-						path="/server/:server/channel/:channel/settings/:page"
-						component={ChannelSettings}
-					/>
-					<Route
-						path="/server/:server/channel/:channel/settings"
-						component={ChannelSettings}
-					/>
-					<Route
-						path="/server/:server/settings/:page"
-						component={ServerSettings}
-					/>
-					<Route
-						path="/server/:server/settings"
-						component={ServerSettings}
-					/>
-					<Route
-						path="/channel/:channel/settings/:page"
-						component={ChannelSettings}
-					/>
-					<Route
-						path="/channel/:channel/settings"
-						component={ChannelSettings}
-					/>
+    return (
+        <OverlappingPanels
+            width="100vw"
+            height="var(--app-height)"
+            leftPanel={
+                inSpecial
+                    ? undefined
+                    : { width: 292, component: <LeftSidebar /> }
+            }
+            rightPanel={
+                !inSettings && inChannel
+                    ? { width: 240, component: <RightSidebar /> }
+                    : undefined
+            }
+            bottomNav={{
+                component: <BottomNavigation />,
+                showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left,
+                height: 50,
+            }}
+            docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
+            <Routes>
+                <Switch>
+                    <Route
+                        path="/server/:server/channel/:channel/settings/:page"
+                        component={ChannelSettings}
+                    />
+                    <Route
+                        path="/server/:server/channel/:channel/settings"
+                        component={ChannelSettings}
+                    />
+                    <Route
+                        path="/server/:server/settings/:page"
+                        component={ServerSettings}
+                    />
+                    <Route
+                        path="/server/:server/settings"
+                        component={ServerSettings}
+                    />
+                    <Route
+                        path="/channel/:channel/settings/:page"
+                        component={ChannelSettings}
+                    />
+                    <Route
+                        path="/channel/:channel/settings"
+                        component={ChannelSettings}
+                    />
 
-					<Route
-						path="/channel/:channel/message/:message"
-						component={Channel}
-					/>
-					<Route
-						path="/server/:server/channel/:channel"
-						component={Channel}
-					/>
-					<Route path="/server/:server" />
-					<Route path="/channel/:channel" component={Channel} />
+                    <Route
+                        path="/channel/:channel/message/:message"
+                        component={Channel}
+                    />
+                    <Route
+                        path="/server/:server/channel/:channel"
+                        component={Channel}
+                    />
+                    <Route path="/server/:server" />
+                    <Route path="/channel/:channel" component={Channel} />
 
-					<Route path="/settings/:page" component={Settings} />
-					<Route path="/settings" component={Settings} />
+                    <Route path="/settings/:page" component={Settings} />
+                    <Route path="/settings" component={Settings} />
 
-					<Route path="/dev" component={Developer} />
-					<Route path="/friends" component={Friends} />
-					<Route path="/open/:id" component={Open} />
-					<Route path="/invite/:code" component={Invite} />
-					<Route path="/" component={Home} />
-				</Switch>
-			</Routes>
-			<ContextMenus />
-			<Popovers />
-			<Notifications />
-			<StateMonitor />
-			<SyncManager />
-		</OverlappingPanels>
-	);
+                    <Route path="/dev" component={Developer} />
+                    <Route path="/friends" component={Friends} />
+                    <Route path="/open/:id" component={Open} />
+                    <Route path="/invite/:code" component={Invite} />
+                    <Route path="/" component={Home} />
+                </Switch>
+            </Routes>
+            <ContextMenus />
+            <Popovers />
+            <Notifications />
+            <StateMonitor />
+            <SyncManager />
+        </OverlappingPanels>
+    );
 }
diff --git a/src/pages/app.tsx b/src/pages/app.tsx
index d5a23a4..57badde 100644
--- a/src/pages/app.tsx
+++ b/src/pages/app.tsx
@@ -12,25 +12,25 @@ const Login = lazy(() => import("./login/Login"));
 const RevoltApp = lazy(() => import("./RevoltApp"));
 
 export function App() {
-	return (
-		<Context>
-			<Masks />
-			{/* 
+    return (
+        <Context>
+            <Masks />
+            {/* 
             // @ts-expect-error */}
-			<Suspense fallback={<Preloader type="spinner" />}>
-				<Switch>
-					<Route path="/login">
-						<CheckAuth>
-							<Login />
-						</CheckAuth>
-					</Route>
-					<Route path="/">
-						<CheckAuth auth>
-							<RevoltApp />
-						</CheckAuth>
-					</Route>
-				</Switch>
-			</Suspense>
-		</Context>
-	);
+            <Suspense fallback={<Preloader type="spinner" />}>
+                <Switch>
+                    <Route path="/login">
+                        <CheckAuth>
+                            <Login />
+                        </CheckAuth>
+                    </Route>
+                    <Route path="/">
+                        <CheckAuth auth>
+                            <RevoltApp />
+                        </CheckAuth>
+                    </Route>
+                </Switch>
+            </Suspense>
+        </Context>
+    );
 }
diff --git a/src/pages/channels/Channel.tsx b/src/pages/channels/Channel.tsx
index 0b4c912..7b459ed 100644
--- a/src/pages/channels/Channel.tsx
+++ b/src/pages/channels/Channel.tsx
@@ -20,134 +20,134 @@ import { MessageArea } from "./messaging/MessageArea";
 import VoiceHeader from "./voice/VoiceHeader";
 
 const ChannelMain = styled.div`
-	flex-grow: 1;
-	display: flex;
-	min-height: 0;
-	overflow: hidden;
-	flex-direction: row;
+    flex-grow: 1;
+    display: flex;
+    min-height: 0;
+    overflow: hidden;
+    flex-direction: row;
 `;
 
 const ChannelContent = styled.div`
-	flex-grow: 1;
-	display: flex;
-	overflow: hidden;
-	flex-direction: column;
+    flex-grow: 1;
+    display: flex;
+    overflow: hidden;
+    flex-direction: column;
 `;
 
 const AgeGate = styled.div`
-	display: flex;
-	flex-grow: 1;
-	flex-direction: column;
-	align-items: center;
-	justify-content: center;
-	user-select: none;
-	padding: 12px;
-
-	img {
-		height: 150px;
-	}
-
-	.subtext {
-		color: var(--secondary-foreground);
-		margin-bottom: 12px;
-		font-size: 14px;
-	}
-
-	.actions {
-		margin-top: 20px;
-		display: flex;
-		gap: 12px;
-	}
+    display: flex;
+    flex-grow: 1;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    user-select: none;
+    padding: 12px;
+
+    img {
+        height: 150px;
+    }
+
+    .subtext {
+        color: var(--secondary-foreground);
+        margin-bottom: 12px;
+        font-size: 14px;
+    }
+
+    .actions {
+        margin-top: 20px;
+        display: flex;
+        gap: 12px;
+    }
 `;
 
 export function Channel({ id }: { id: string }) {
-	const ctx = useForceUpdate();
-	const channel = useChannel(id, ctx);
+    const ctx = useForceUpdate();
+    const channel = useChannel(id, ctx);
 
-	if (!channel) return null;
+    if (!channel) return null;
 
-	if (channel.channel_type === "VoiceChannel") {
-		return <VoiceChannel channel={channel} />;
-	} else {
-		return <TextChannel channel={channel} />;
-	}
+    if (channel.channel_type === "VoiceChannel") {
+        return <VoiceChannel channel={channel} />;
+    } else {
+        return <TextChannel channel={channel} />;
+    }
 }
 
 function TextChannel({ channel }: { channel: Channels.Channel }) {
-	const [showMembers, setMembers] = useState(true);
-
-	if (
-		(channel.channel_type === "TextChannel" ||
-			channel.channel_type === "Group") &&
-		channel.name.includes("nsfw")
-	) {
-		const goBack = useHistory();
-		const [consent, setConsent] = useState(false);
-		const [ageGate, setAgeGate] = useState(false);
-		if (!ageGate) {
-			return (
-				<AgeGate>
-					<img
-						src={"https://static.revolt.chat/emoji/mutant/26a0.svg"}
-						draggable={false}
-					/>
-					<h2>{channel.name}</h2>
-					<span className="subtext">
-						This channel is marked as NSFW.{" "}
-						<a href="#">Learn more</a>
-					</span>
-
-					<Checkbox checked={consent} onChange={(v) => setConsent(v)}>
-						I confirm that I am at least 18 years old.
-					</Checkbox>
-					<div className="actions">
-						<Button contrast onClick={() => goBack}>
-							Go back
-						</Button>
-						<Button
-							contrast
-							onClick={() => consent && setAgeGate(true)}>
-							Enter Channel
-						</Button>
-					</div>
-				</AgeGate>
-			);
-		}
-	}
-
-	let id = channel._id;
-	return (
-		<>
-			<ChannelHeader
-				channel={channel}
-				toggleSidebar={() => setMembers(!showMembers)}
-			/>
-			<ChannelMain>
-				<ChannelContent>
-					<VoiceHeader id={id} />
-					<MessageArea id={id} />
-					<TypingIndicator id={id} />
-					<JumpToBottom id={id} />
-					<MessageBox channel={channel} />
-				</ChannelContent>
-				{!isTouchscreenDevice && showMembers && (
-					<MemberSidebar channel={channel} />
-				)}
-			</ChannelMain>
-		</>
-	);
+    const [showMembers, setMembers] = useState(true);
+
+    if (
+        (channel.channel_type === "TextChannel" ||
+            channel.channel_type === "Group") &&
+        channel.name.includes("nsfw")
+    ) {
+        const goBack = useHistory();
+        const [consent, setConsent] = useState(false);
+        const [ageGate, setAgeGate] = useState(false);
+        if (!ageGate) {
+            return (
+                <AgeGate>
+                    <img
+                        src={"https://static.revolt.chat/emoji/mutant/26a0.svg"}
+                        draggable={false}
+                    />
+                    <h2>{channel.name}</h2>
+                    <span className="subtext">
+                        This channel is marked as NSFW.{" "}
+                        <a href="#">Learn more</a>
+                    </span>
+
+                    <Checkbox checked={consent} onChange={(v) => setConsent(v)}>
+                        I confirm that I am at least 18 years old.
+                    </Checkbox>
+                    <div className="actions">
+                        <Button contrast onClick={() => goBack}>
+                            Go back
+                        </Button>
+                        <Button
+                            contrast
+                            onClick={() => consent && setAgeGate(true)}>
+                            Enter Channel
+                        </Button>
+                    </div>
+                </AgeGate>
+            );
+        }
+    }
+
+    let id = channel._id;
+    return (
+        <>
+            <ChannelHeader
+                channel={channel}
+                toggleSidebar={() => setMembers(!showMembers)}
+            />
+            <ChannelMain>
+                <ChannelContent>
+                    <VoiceHeader id={id} />
+                    <MessageArea id={id} />
+                    <TypingIndicator id={id} />
+                    <JumpToBottom id={id} />
+                    <MessageBox channel={channel} />
+                </ChannelContent>
+                {!isTouchscreenDevice && showMembers && (
+                    <MemberSidebar channel={channel} />
+                )}
+            </ChannelMain>
+        </>
+    );
 }
 
 function VoiceChannel({ channel }: { channel: Channels.Channel }) {
-	return (
-		<>
-			<ChannelHeader channel={channel} />
-			<VoiceHeader id={channel._id} />
-		</>
-	);
+    return (
+        <>
+            <ChannelHeader channel={channel} />
+            <VoiceHeader id={channel._id} />
+        </>
+    );
 }
 
 export default function () {
-	const { channel } = useParams<{ channel: string }>();
-	return <Channel id={channel} key={channel} />;
+    const { channel } = useParams<{ channel: string }>();
+    return <Channel id={channel} key={channel} />;
 }
diff --git a/src/pages/channels/ChannelHeader.tsx b/src/pages/channels/ChannelHeader.tsx
index 7a07369..e26a472 100644
--- a/src/pages/channels/ChannelHeader.tsx
+++ b/src/pages/channels/ChannelHeader.tsx
@@ -19,121 +19,121 @@ import Markdown from "../../components/markdown/Markdown";
 import HeaderActions from "./actions/HeaderActions";
 
 export interface ChannelHeaderProps {
-	channel: Channel;
-	toggleSidebar?: () => void;
+    channel: Channel;
+    toggleSidebar?: () => void;
 }
 
 const Info = styled.div`
-	flex-grow: 1;
-	min-width: 0;
-	overflow: hidden;
-	white-space: nowrap;
+    flex-grow: 1;
+    min-width: 0;
+    overflow: hidden;
+    white-space: nowrap;
 
-	display: flex;
-	gap: 8px;
-	align-items: center;
+    display: flex;
+    gap: 8px;
+    align-items: center;
 
-	* {
-		display: inline-block;
-	}
+    * {
+        display: inline-block;
+    }
 
-	.divider {
-		height: 20px;
-		margin: 0 5px;
-		padding-left: 1px;
-		background-color: var(--tertiary-background);
-	}
+    .divider {
+        height: 20px;
+        margin: 0 5px;
+        padding-left: 1px;
+        background-color: var(--tertiary-background);
+    }
 
-	.status {
-		width: 10px;
-		height: 10px;
-		border-radius: 50%;
-		display: inline-block;
-		margin-inline-end: 6px;
-	}
+    .status {
+        width: 10px;
+        height: 10px;
+        border-radius: 50%;
+        display: inline-block;
+        margin-inline-end: 6px;
+    }
 
-	.desc {
-		cursor: pointer;
-		margin-top: 2px;
-		font-size: 0.8em;
-		font-weight: 400;
-		color: var(--secondary-foreground);
-	}
+    .desc {
+        cursor: pointer;
+        margin-top: 2px;
+        font-size: 0.8em;
+        font-weight: 400;
+        color: var(--secondary-foreground);
+    }
 `;
 
 export default function ChannelHeader({
-	channel,
-	toggleSidebar,
+    channel,
+    toggleSidebar,
 }: ChannelHeaderProps) {
-	const { openScreen } = useIntermediate();
-	const client = useContext(AppContext);
+    const { openScreen } = useIntermediate();
+    const client = useContext(AppContext);
 
-	const name = getChannelName(client, channel);
-	let icon, recipient;
-	switch (channel.channel_type) {
-		case "SavedMessages":
-			icon = <Notepad size={24} />;
-			break;
-		case "DirectMessage":
-			icon = <At size={24} />;
-			const uid = client.channels.getRecipient(channel._id);
-			recipient = client.users.get(uid);
-			break;
-		case "Group":
-			icon = <Group size={24} />;
-			break;
-		case "TextChannel":
-			icon = <Hash size={24} />;
-			break;
-	}
+    const name = getChannelName(client, channel);
+    let icon, recipient;
+    switch (channel.channel_type) {
+        case "SavedMessages":
+            icon = <Notepad size={24} />;
+            break;
+        case "DirectMessage":
+            icon = <At size={24} />;
+            const uid = client.channels.getRecipient(channel._id);
+            recipient = client.users.get(uid);
+            break;
+        case "Group":
+            icon = <Group size={24} />;
+            break;
+        case "TextChannel":
+            icon = <Hash size={24} />;
+            break;
+    }
 
-	return (
-		<Header placement="primary">
-			{icon}
-			<Info>
-				<span className="name">{name}</span>
-				{isTouchscreenDevice &&
-					channel.channel_type === "DirectMessage" && (
-						<>
-							<div className="divider" />
-							<span className="desc">
-								<div
-									className="status"
-									style={{
-										backgroundColor: useStatusColour(
-											recipient as User,
-										),
-									}}
-								/>
-								<UserStatus user={recipient as User} />
-							</span>
-						</>
-					)}
-				{!isTouchscreenDevice &&
-					(channel.channel_type === "Group" ||
-						channel.channel_type === "TextChannel") &&
-					channel.description && (
-						<>
-							<div className="divider" />
-							<span
-								className="desc"
-								onClick={() =>
-									openScreen({
-										id: "channel_info",
-										channel_id: channel._id,
-									})
-								}>
-								<Markdown
-									content={
-										channel.description.split("\n")[0] ?? ""
-									}
-									disallowBigEmoji
-								/>
-							</span>
-						</>
-					)}
-			</Info>
-			<HeaderActions channel={channel} toggleSidebar={toggleSidebar} />
-		</Header>
-	);
+    return (
+        <Header placement="primary">
+            {icon}
+            <Info>
+                <span className="name">{name}</span>
+                {isTouchscreenDevice &&
+                    channel.channel_type === "DirectMessage" && (
+                        <>
+                            <div className="divider" />
+                            <span className="desc">
+                                <div
+                                    className="status"
+                                    style={{
+                                        backgroundColor: useStatusColour(
+                                            recipient as User,
+                                        ),
+                                    }}
+                                />
+                                <UserStatus user={recipient as User} />
+                            </span>
+                        </>
+                    )}
+                {!isTouchscreenDevice &&
+                    (channel.channel_type === "Group" ||
+                        channel.channel_type === "TextChannel") &&
+                    channel.description && (
+                        <>
+                            <div className="divider" />
+                            <span
+                                className="desc"
+                                onClick={() =>
+                                    openScreen({
+                                        id: "channel_info",
+                                        channel_id: channel._id,
+                                    })
+                                }>
+                                <Markdown
+                                    content={
+                                        channel.description.split("\n")[0] ?? ""
+                                    }
+                                    disallowBigEmoji
+                                />
+                            </span>
+                        </>
+                    )}
+            </Info>
+            <HeaderActions channel={channel} toggleSidebar={toggleSidebar} />
+        </Header>
+    );
 }
diff --git a/src/pages/channels/actions/HeaderActions.tsx b/src/pages/channels/actions/HeaderActions.tsx
index 096835d..dcb2ed0 100644
--- a/src/pages/channels/actions/HeaderActions.tsx
+++ b/src/pages/channels/actions/HeaderActions.tsx
@@ -1,9 +1,9 @@
 import { Sidebar as SidebarIcon } from "@styled-icons/boxicons-regular";
 import {
-	UserPlus,
-	Cog,
-	PhoneCall,
-	PhoneOutgoing,
+    UserPlus,
+    Cog,
+    PhoneCall,
+    PhoneOutgoing,
 } from "@styled-icons/boxicons-solid";
 import { useHistory } from "react-router-dom";
 
@@ -12,9 +12,9 @@ import { useContext } from "preact/hooks";
 import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
 
 import {
-	VoiceContext,
-	VoiceOperationsContext,
-	VoiceStatus,
+    VoiceContext,
+    VoiceOperationsContext,
+    VoiceStatus,
 } from "../../../context/Voice";
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
@@ -25,88 +25,88 @@ import IconButton from "../../../components/ui/IconButton";
 import { ChannelHeaderProps } from "../ChannelHeader";
 
 export default function HeaderActions({
-	channel,
-	toggleSidebar,
+    channel,
+    toggleSidebar,
 }: ChannelHeaderProps) {
-	const { openScreen } = useIntermediate();
-	const client = useContext(AppContext);
-	const history = useHistory();
+    const { openScreen } = useIntermediate();
+    const client = useContext(AppContext);
+    const history = useHistory();
 
-	return (
-		<>
-			<UpdateIndicator />
-			{channel.channel_type === "Group" && (
-				<>
-					<IconButton
-						onClick={() =>
-							openScreen({
-								id: "user_picker",
-								omit: channel.recipients,
-								callback: async (users) => {
-									for (const user of users) {
-										await client.channels.addMember(
-											channel._id,
-											user,
-										);
-									}
-								},
-							})
-						}>
-						<UserPlus size={27} />
-					</IconButton>
-					<IconButton
-						onClick={() =>
-							history.push(`/channel/${channel._id}/settings`)
-						}>
-						<Cog size={24} />
-					</IconButton>
-				</>
-			)}
-			<VoiceActions channel={channel} />
-			{(channel.channel_type === "Group" ||
-				channel.channel_type === "TextChannel") &&
-				!isTouchscreenDevice && (
-					<IconButton onClick={toggleSidebar}>
-						<SidebarIcon size={22} />
-					</IconButton>
-				)}
-		</>
-	);
+    return (
+        <>
+            <UpdateIndicator />
+            {channel.channel_type === "Group" && (
+                <>
+                    <IconButton
+                        onClick={() =>
+                            openScreen({
+                                id: "user_picker",
+                                omit: channel.recipients,
+                                callback: async (users) => {
+                                    for (const user of users) {
+                                        await client.channels.addMember(
+                                            channel._id,
+                                            user,
+                                        );
+                                    }
+                                },
+                            })
+                        }>
+                        <UserPlus size={27} />
+                    </IconButton>
+                    <IconButton
+                        onClick={() =>
+                            history.push(`/channel/${channel._id}/settings`)
+                        }>
+                        <Cog size={24} />
+                    </IconButton>
+                </>
+            )}
+            <VoiceActions channel={channel} />
+            {(channel.channel_type === "Group" ||
+                channel.channel_type === "TextChannel") &&
+                !isTouchscreenDevice && (
+                    <IconButton onClick={toggleSidebar}>
+                        <SidebarIcon size={22} />
+                    </IconButton>
+                )}
+        </>
+    );
 }
 
 function VoiceActions({ channel }: Pick<ChannelHeaderProps, "channel">) {
-	if (
-		channel.channel_type === "SavedMessages" ||
-		channel.channel_type === "TextChannel"
-	)
-		return null;
+    if (
+        channel.channel_type === "SavedMessages" ||
+        channel.channel_type === "TextChannel"
+    )
+        return null;
 
-	const voice = useContext(VoiceContext);
-	const { connect, disconnect } = useContext(VoiceOperationsContext);
+    const voice = useContext(VoiceContext);
+    const { connect, disconnect } = useContext(VoiceOperationsContext);
 
-	if (voice.status >= VoiceStatus.READY) {
-		if (voice.roomId === channel._id) {
-			return (
-				<IconButton onClick={disconnect}>
-					<PhoneOutgoing size={22} />
-				</IconButton>
-			);
-		} else {
-			return (
-				<IconButton
-					onClick={() => {
-						disconnect();
-						connect(channel._id);
-					}}>
-					<PhoneCall size={24} />
-				</IconButton>
-			);
-		}
-	} else {
-		return (
-			<IconButton>
-				<PhoneCall size={24} /** ! FIXME: TEMP */ color="red" />
-			</IconButton>
-		);
-	}
+    if (voice.status >= VoiceStatus.READY) {
+        if (voice.roomId === channel._id) {
+            return (
+                <IconButton onClick={disconnect}>
+                    <PhoneOutgoing size={22} />
+                </IconButton>
+            );
+        } else {
+            return (
+                <IconButton
+                    onClick={() => {
+                        disconnect();
+                        connect(channel._id);
+                    }}>
+                    <PhoneCall size={24} />
+                </IconButton>
+            );
+        }
+    } else {
+        return (
+            <IconButton>
+                <PhoneCall size={24} /** ! FIXME: TEMP */ color="red" />
+            </IconButton>
+        );
+    }
 }
diff --git a/src/pages/channels/messaging/ConversationStart.tsx b/src/pages/channels/messaging/ConversationStart.tsx
index 9fe82d7..acaef3c 100644
--- a/src/pages/channels/messaging/ConversationStart.tsx
+++ b/src/pages/channels/messaging/ConversationStart.tsx
@@ -6,35 +6,35 @@ import { useChannel, useForceUpdate } from "../../../context/revoltjs/hooks";
 import { getChannelName } from "../../../context/revoltjs/util";
 
 const StartBase = styled.div`
-	margin: 18px 16px 10px 16px;
-
-	h1 {
-		font-size: 23px;
-		margin: 0 0 8px 0;
-	}
-
-	h4 {
-		font-weight: 400;
-		margin: 0;
-		font-size: 14px;
-	}
+    margin: 18px 16px 10px 16px;
+
+    h1 {
+        font-size: 23px;
+        margin: 0 0 8px 0;
+    }
+
+    h4 {
+        font-weight: 400;
+        margin: 0;
+        font-size: 14px;
+    }
 `;
 
 interface Props {
-	id: string;
+    id: string;
 }
 
 export default function ConversationStart({ id }: Props) {
-	const ctx = useForceUpdate();
-	const channel = useChannel(id, ctx);
-	if (!channel) return null;
-
-	return (
-		<StartBase>
-			<h1>{getChannelName(ctx.client, channel, true)}</h1>
-			<h4>
-				<Text id="app.main.channel.start.group" />
-			</h4>
-		</StartBase>
-	);
+    const ctx = useForceUpdate();
+    const channel = useChannel(id, ctx);
+    if (!channel) return null;
+
+    return (
+        <StartBase>
+            <h1>{getChannelName(ctx.client, channel, true)}</h1>
+            <h4>
+                <Text id="app.main.channel.start.group" />
+            </h4>
+        </StartBase>
+    );
 }
diff --git a/src/pages/channels/messaging/MessageArea.tsx b/src/pages/channels/messaging/MessageArea.tsx
index 7fe720d..1b0ad40 100644
--- a/src/pages/channels/messaging/MessageArea.tsx
+++ b/src/pages/channels/messaging/MessageArea.tsx
@@ -4,11 +4,11 @@ import useResizeObserver from "use-resize-observer";
 
 import { createContext } from "preact";
 import {
-	useContext,
-	useEffect,
-	useLayoutEffect,
-	useRef,
-	useState,
+    useContext,
+    useEffect,
+    useLayoutEffect,
+    useRef,
+    useState,
 } from "preact/hooks";
 
 import { defer } from "../../../lib/defer";
@@ -19,8 +19,8 @@ import { RenderState, ScrollState } from "../../../lib/renderer/types";
 import { IntermediateContext } from "../../../context/intermediate/Intermediate";
 import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
 import {
-	ClientStatus,
-	StatusContext,
+    ClientStatus,
+    StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
 
 import Preloader from "../../../components/ui/Preloader";
@@ -29,231 +29,231 @@ import ConversationStart from "./ConversationStart";
 import MessageRenderer from "./MessageRenderer";
 
 const Area = styled.div`
-	height: 100%;
-	flex-grow: 1;
-	min-height: 0;
-	overflow-x: hidden;
-	overflow-y: scroll;
-	word-break: break-word;
-
-	> div {
-		display: flex;
-		min-height: 100%;
-		padding-bottom: 20px;
-		flex-direction: column;
-		justify-content: flex-end;
-	}
+    height: 100%;
+    flex-grow: 1;
+    min-height: 0;
+    overflow-x: hidden;
+    overflow-y: scroll;
+    word-break: break-word;
+
+    > div {
+        display: flex;
+        min-height: 100%;
+        padding-bottom: 20px;
+        flex-direction: column;
+        justify-content: flex-end;
+    }
 `;
 
 interface Props {
-	id: string;
+    id: string;
 }
 
 export const MessageAreaWidthContext = createContext(0);
 export const MESSAGE_AREA_PADDING = 82;
 
 export function MessageArea({ id }: Props) {
-	const status = useContext(StatusContext);
-	const { focusTaken } = useContext(IntermediateContext);
-
-	// ? This is the scroll container.
-	const ref = useRef<HTMLDivElement>(null);
-	const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
-
-	// ? Current channel state.
-	const [state, setState] = useState<RenderState>({ type: "LOADING" });
-
-	// ? useRef to avoid re-renders
-	const scrollState = useRef<ScrollState>({ type: "Free" });
-
-	const setScrollState = (v: ScrollState) => {
-		if (v.type === "StayAtBottom") {
-			if (scrollState.current.type === "Bottom" || atBottom()) {
-				scrollState.current = {
-					type: "ScrollToBottom",
-					smooth: v.smooth,
-				};
-			} else {
-				scrollState.current = { type: "Free" };
-			}
-		} else {
-			scrollState.current = v;
-		}
-
-		defer(() => {
-			if (scrollState.current.type === "ScrollToBottom") {
-				setScrollState({
-					type: "Bottom",
-					scrollingUntil: +new Date() + 150,
-				});
-
-				animateScroll.scrollToBottom({
-					container: ref.current,
-					duration: scrollState.current.smooth ? 150 : 0,
-				});
-			} else if (scrollState.current.type === "OffsetTop") {
-				animateScroll.scrollTo(
-					Math.max(
-						101,
-						ref.current.scrollTop +
-							(ref.current.scrollHeight -
-								scrollState.current.previousHeight),
-					),
-					{
-						container: ref.current,
-						duration: 0,
-					},
-				);
-
-				setScrollState({ type: "Free" });
-			} else if (scrollState.current.type === "ScrollTop") {
-				animateScroll.scrollTo(scrollState.current.y, {
-					container: ref.current,
-					duration: 0,
-				});
-
-				setScrollState({ type: "Free" });
-			}
-		});
-	};
-
-	// ? Determine if we are at the bottom of the scroll container.
-	// -> https://stackoverflow.com/a/44893438
-	// By default, we assume we are at the bottom, i.e. when we first load.
-	const atBottom = (offset = 0) =>
-		ref.current
-			? Math.floor(ref.current.scrollHeight - ref.current.scrollTop) -
-					offset <=
-			  ref.current.clientHeight
-			: true;
-
-	const atTop = (offset = 0) => ref.current.scrollTop <= offset;
-
-	// ? Handle events from renderer.
-	useEffect(() => {
-		SingletonMessageRenderer.addListener("state", setState);
-		return () => SingletonMessageRenderer.removeListener("state", setState);
-	}, []);
-
-	useEffect(() => {
-		SingletonMessageRenderer.addListener("scroll", setScrollState);
-		return () =>
-			SingletonMessageRenderer.removeListener("scroll", setScrollState);
-	}, [scrollState]);
-
-	// ? Load channel initially.
-	useEffect(() => {
-		SingletonMessageRenderer.init(id);
-	}, [id]);
-
-	// ? If we are waiting for network, try again.
-	useEffect(() => {
-		switch (status) {
-			case ClientStatus.ONLINE:
-				if (state.type === "WAITING_FOR_NETWORK") {
-					SingletonMessageRenderer.init(id);
-				} else {
-					SingletonMessageRenderer.reloadStale(id);
-				}
-
-				break;
-			case ClientStatus.OFFLINE:
-			case ClientStatus.DISCONNECTED:
-			case ClientStatus.CONNECTING:
-				SingletonMessageRenderer.markStale();
-				break;
-		}
-	}, [status, state]);
-
-	// ? When the container is scrolled.
-	// ? Also handle StayAtBottom
-	useEffect(() => {
-		async function onScroll() {
-			if (scrollState.current.type === "Free" && atBottom()) {
-				setScrollState({ type: "Bottom" });
-			} else if (scrollState.current.type === "Bottom" && !atBottom()) {
-				if (
-					scrollState.current.scrollingUntil &&
-					scrollState.current.scrollingUntil > +new Date()
-				)
-					return;
-				setScrollState({ type: "Free" });
-			}
-		}
-
-		ref.current.addEventListener("scroll", onScroll);
-		return () => ref.current.removeEventListener("scroll", onScroll);
-	}, [ref, scrollState]);
-
-	// ? Top and bottom loaders.
-	useEffect(() => {
-		async function onScroll() {
-			if (atTop(100)) {
-				SingletonMessageRenderer.loadTop(ref.current);
-			}
-
-			if (atBottom(100)) {
-				SingletonMessageRenderer.loadBottom(ref.current);
-			}
-		}
-
-		ref.current.addEventListener("scroll", onScroll);
-		return () => ref.current.removeEventListener("scroll", onScroll);
-	}, [ref]);
-
-	// ? Scroll down whenever the message area resizes.
-	function stbOnResize() {
-		if (!atBottom() && scrollState.current.type === "Bottom") {
-			animateScroll.scrollToBottom({
-				container: ref.current,
-				duration: 0,
-			});
-
-			setScrollState({ type: "Bottom" });
-		}
-	}
-
-	// ? Scroll down when container resized.
-	useLayoutEffect(() => {
-		stbOnResize();
-	}, [height]);
-
-	// ? Scroll down whenever the window resizes.
-	useLayoutEffect(() => {
-		document.addEventListener("resize", stbOnResize);
-		return () => document.removeEventListener("resize", stbOnResize);
-	}, [ref, scrollState]);
-
-	// ? Scroll to bottom when pressing 'Escape'.
-	useEffect(() => {
-		function keyUp(e: KeyboardEvent) {
-			if (e.key === "Escape" && !focusTaken) {
-				SingletonMessageRenderer.jumpToBottom(id, true);
-				internalEmit("TextArea", "focus", "message");
-			}
-		}
-
-		document.body.addEventListener("keyup", keyUp);
-		return () => document.body.removeEventListener("keyup", keyUp);
-	}, [ref, focusTaken]);
-
-	return (
-		<MessageAreaWidthContext.Provider
-			value={(width ?? 0) - MESSAGE_AREA_PADDING}>
-			<Area ref={ref}>
-				<div>
-					{state.type === "LOADING" && <Preloader type="ring" />}
-					{state.type === "WAITING_FOR_NETWORK" && (
-						<RequiresOnline>
-							<Preloader type="ring" />
-						</RequiresOnline>
-					)}
-					{state.type === "RENDER" && (
-						<MessageRenderer id={id} state={state} />
-					)}
-					{state.type === "EMPTY" && <ConversationStart id={id} />}
-				</div>
-			</Area>
-		</MessageAreaWidthContext.Provider>
-	);
+    const status = useContext(StatusContext);
+    const { focusTaken } = useContext(IntermediateContext);
+
+    // ? This is the scroll container.
+    const ref = useRef<HTMLDivElement>(null);
+    const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
+
+    // ? Current channel state.
+    const [state, setState] = useState<RenderState>({ type: "LOADING" });
+
+    // ? useRef to avoid re-renders
+    const scrollState = useRef<ScrollState>({ type: "Free" });
+
+    const setScrollState = (v: ScrollState) => {
+        if (v.type === "StayAtBottom") {
+            if (scrollState.current.type === "Bottom" || atBottom()) {
+                scrollState.current = {
+                    type: "ScrollToBottom",
+                    smooth: v.smooth,
+                };
+            } else {
+                scrollState.current = { type: "Free" };
+            }
+        } else {
+            scrollState.current = v;
+        }
+
+        defer(() => {
+            if (scrollState.current.type === "ScrollToBottom") {
+                setScrollState({
+                    type: "Bottom",
+                    scrollingUntil: +new Date() + 150,
+                });
+
+                animateScroll.scrollToBottom({
+                    container: ref.current,
+                    duration: scrollState.current.smooth ? 150 : 0,
+                });
+            } else if (scrollState.current.type === "OffsetTop") {
+                animateScroll.scrollTo(
+                    Math.max(
+                        101,
+                        ref.current.scrollTop +
+                            (ref.current.scrollHeight -
+                                scrollState.current.previousHeight),
+                    ),
+                    {
+                        container: ref.current,
+                        duration: 0,
+                    },
+                );
+
+                setScrollState({ type: "Free" });
+            } else if (scrollState.current.type === "ScrollTop") {
+                animateScroll.scrollTo(scrollState.current.y, {
+                    container: ref.current,
+                    duration: 0,
+                });
+
+                setScrollState({ type: "Free" });
+            }
+        });
+    };
+
+    // ? Determine if we are at the bottom of the scroll container.
+    // -> https://stackoverflow.com/a/44893438
+    // By default, we assume we are at the bottom, i.e. when we first load.
+    const atBottom = (offset = 0) =>
+        ref.current
+            ? Math.floor(ref.current.scrollHeight - ref.current.scrollTop) -
+                  offset <=
+              ref.current.clientHeight
+            : true;
+
+    const atTop = (offset = 0) => ref.current.scrollTop <= offset;
+
+    // ? Handle events from renderer.
+    useEffect(() => {
+        SingletonMessageRenderer.addListener("state", setState);
+        return () => SingletonMessageRenderer.removeListener("state", setState);
+    }, []);
+
+    useEffect(() => {
+        SingletonMessageRenderer.addListener("scroll", setScrollState);
+        return () =>
+            SingletonMessageRenderer.removeListener("scroll", setScrollState);
+    }, [scrollState]);
+
+    // ? Load channel initially.
+    useEffect(() => {
+        SingletonMessageRenderer.init(id);
+    }, [id]);
+
+    // ? If we are waiting for network, try again.
+    useEffect(() => {
+        switch (status) {
+            case ClientStatus.ONLINE:
+                if (state.type === "WAITING_FOR_NETWORK") {
+                    SingletonMessageRenderer.init(id);
+                } else {
+                    SingletonMessageRenderer.reloadStale(id);
+                }
+
+                break;
+            case ClientStatus.OFFLINE:
+            case ClientStatus.DISCONNECTED:
+            case ClientStatus.CONNECTING:
+                SingletonMessageRenderer.markStale();
+                break;
+        }
+    }, [status, state]);
+
+    // ? When the container is scrolled.
+    // ? Also handle StayAtBottom
+    useEffect(() => {
+        async function onScroll() {
+            if (scrollState.current.type === "Free" && atBottom()) {
+                setScrollState({ type: "Bottom" });
+            } else if (scrollState.current.type === "Bottom" && !atBottom()) {
+                if (
+                    scrollState.current.scrollingUntil &&
+                    scrollState.current.scrollingUntil > +new Date()
+                )
+                    return;
+                setScrollState({ type: "Free" });
+            }
+        }
+
+        ref.current.addEventListener("scroll", onScroll);
+        return () => ref.current.removeEventListener("scroll", onScroll);
+    }, [ref, scrollState]);
+
+    // ? Top and bottom loaders.
+    useEffect(() => {
+        async function onScroll() {
+            if (atTop(100)) {
+                SingletonMessageRenderer.loadTop(ref.current);
+            }
+
+            if (atBottom(100)) {
+                SingletonMessageRenderer.loadBottom(ref.current);
+            }
+        }
+
+        ref.current.addEventListener("scroll", onScroll);
+        return () => ref.current.removeEventListener("scroll", onScroll);
+    }, [ref]);
+
+    // ? Scroll down whenever the message area resizes.
+    function stbOnResize() {
+        if (!atBottom() && scrollState.current.type === "Bottom") {
+            animateScroll.scrollToBottom({
+                container: ref.current,
+                duration: 0,
+            });
+
+            setScrollState({ type: "Bottom" });
+        }
+    }
+
+    // ? Scroll down when container resized.
+    useLayoutEffect(() => {
+        stbOnResize();
+    }, [height]);
+
+    // ? Scroll down whenever the window resizes.
+    useLayoutEffect(() => {
+        document.addEventListener("resize", stbOnResize);
+        return () => document.removeEventListener("resize", stbOnResize);
+    }, [ref, scrollState]);
+
+    // ? Scroll to bottom when pressing 'Escape'.
+    useEffect(() => {
+        function keyUp(e: KeyboardEvent) {
+            if (e.key === "Escape" && !focusTaken) {
+                SingletonMessageRenderer.jumpToBottom(id, true);
+                internalEmit("TextArea", "focus", "message");
+            }
+        }
+
+        document.body.addEventListener("keyup", keyUp);
+        return () => document.body.removeEventListener("keyup", keyUp);
+    }, [ref, focusTaken]);
+
+    return (
+        <MessageAreaWidthContext.Provider
+            value={(width ?? 0) - MESSAGE_AREA_PADDING}>
+            <Area ref={ref}>
+                <div>
+                    {state.type === "LOADING" && <Preloader type="ring" />}
+                    {state.type === "WAITING_FOR_NETWORK" && (
+                        <RequiresOnline>
+                            <Preloader type="ring" />
+                        </RequiresOnline>
+                    )}
+                    {state.type === "RENDER" && (
+                        <MessageRenderer id={id} state={state} />
+                    )}
+                    {state.type === "EMPTY" && <ConversationStart id={id} />}
+                </div>
+            </Area>
+        </MessageAreaWidthContext.Provider>
+    );
 }
diff --git a/src/pages/channels/messaging/MessageEditor.tsx b/src/pages/channels/messaging/MessageEditor.tsx
index 78fe70b..3aa1a43 100644
--- a/src/pages/channels/messaging/MessageEditor.tsx
+++ b/src/pages/channels/messaging/MessageEditor.tsx
@@ -6,128 +6,128 @@ import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
 import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
 
 import {
-	IntermediateContext,
-	useIntermediate,
+    IntermediateContext,
+    useIntermediate,
 } from "../../../context/intermediate/Intermediate";
 import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { MessageObject } from "../../../context/revoltjs/util";
 
 import AutoComplete, {
-	useAutoComplete,
+    useAutoComplete,
 } from "../../../components/common/AutoComplete";
 
 const EditorBase = styled.div`
-	display: flex;
-	flex-direction: column;
-
-	textarea {
-		resize: none;
-		padding: 12px;
-		font-size: 0.875rem;
-		border-radius: 3px;
-		white-space: pre-wrap;
-		background: var(--secondary-header);
-	}
-
-	.caption {
-		padding: 2px;
-		font-size: 11px;
-		color: var(--tertiary-foreground);
-
-		a {
-			cursor: pointer;
-			&:hover {
-				text-decoration: underline;
-			}
-		}
-	}
+    display: flex;
+    flex-direction: column;
+
+    textarea {
+        resize: none;
+        padding: 12px;
+        font-size: 0.875rem;
+        border-radius: 3px;
+        white-space: pre-wrap;
+        background: var(--secondary-header);
+    }
+
+    .caption {
+        padding: 2px;
+        font-size: 11px;
+        color: var(--tertiary-foreground);
+
+        a {
+            cursor: pointer;
+            &:hover {
+                text-decoration: underline;
+            }
+        }
+    }
 `;
 
 interface Props {
-	message: MessageObject;
-	finish: () => void;
+    message: MessageObject;
+    finish: () => void;
 }
 
 export default function MessageEditor({ message, finish }: Props) {
-	const [content, setContent] = useState((message.content as string) ?? "");
-	const { focusTaken } = useContext(IntermediateContext);
-	const { openScreen } = useIntermediate();
-	const client = useContext(AppContext);
-
-	async function save() {
-		finish();
-
-		if (content.length === 0) {
-			openScreen({
-				id: "special_prompt",
-				// @ts-expect-error
-				type: "delete_message",
-				// @ts-expect-error
-				target: message,
-			});
-		} else if (content !== message.content) {
-			await client.channels.editMessage(message.channel, message._id, {
-				content,
-			});
-		}
-	}
-
-	// ? Stop editing when pressing ESC.
-	useEffect(() => {
-		function keyUp(e: KeyboardEvent) {
-			if (e.key === "Escape" && !focusTaken) {
-				finish();
-			}
-		}
-
-		document.body.addEventListener("keyup", keyUp);
-		return () => document.body.removeEventListener("keyup", keyUp);
-	}, [focusTaken]);
-
-	const {
-		onChange,
-		onKeyUp,
-		onKeyDown,
-		onFocus,
-		onBlur,
-		...autoCompleteProps
-	} = useAutoComplete((v) => setContent(v ?? ""), {
-		users: { type: "all" },
-	});
-
-	return (
-		<EditorBase>
-			<AutoComplete detached {...autoCompleteProps} />
-			<TextAreaAutoSize
-				forceFocus
-				maxRows={3}
-				padding={12}
-				value={content}
-				maxLength={2000}
-				onChange={(ev) => {
-					onChange(ev);
-					setContent(ev.currentTarget.value);
-				}}
-				onKeyDown={(e) => {
-					if (onKeyDown(e)) return;
-
-					if (
-						!e.shiftKey &&
-						e.key === "Enter" &&
-						!isTouchscreenDevice
-					) {
-						e.preventDefault();
-						save();
-					}
-				}}
-				onKeyUp={onKeyUp}
-				onFocus={onFocus}
-				onBlur={onBlur}
-			/>
-			<span className="caption">
-				escape to <a onClick={finish}>cancel</a> &middot; enter to{" "}
-				<a onClick={save}>save</a>
-			</span>
-		</EditorBase>
-	);
+    const [content, setContent] = useState((message.content as string) ?? "");
+    const { focusTaken } = useContext(IntermediateContext);
+    const { openScreen } = useIntermediate();
+    const client = useContext(AppContext);
+
+    async function save() {
+        finish();
+
+        if (content.length === 0) {
+            openScreen({
+                id: "special_prompt",
+                // @ts-expect-error
+                type: "delete_message",
+                // @ts-expect-error
+                target: message,
+            });
+        } else if (content !== message.content) {
+            await client.channels.editMessage(message.channel, message._id, {
+                content,
+            });
+        }
+    }
+
+    // ? Stop editing when pressing ESC.
+    useEffect(() => {
+        function keyUp(e: KeyboardEvent) {
+            if (e.key === "Escape" && !focusTaken) {
+                finish();
+            }
+        }
+
+        document.body.addEventListener("keyup", keyUp);
+        return () => document.body.removeEventListener("keyup", keyUp);
+    }, [focusTaken]);
+
+    const {
+        onChange,
+        onKeyUp,
+        onKeyDown,
+        onFocus,
+        onBlur,
+        ...autoCompleteProps
+    } = useAutoComplete((v) => setContent(v ?? ""), {
+        users: { type: "all" },
+    });
+
+    return (
+        <EditorBase>
+            <AutoComplete detached {...autoCompleteProps} />
+            <TextAreaAutoSize
+                forceFocus
+                maxRows={3}
+                padding={12}
+                value={content}
+                maxLength={2000}
+                onChange={(ev) => {
+                    onChange(ev);
+                    setContent(ev.currentTarget.value);
+                }}
+                onKeyDown={(e) => {
+                    if (onKeyDown(e)) return;
+
+                    if (
+                        !e.shiftKey &&
+                        e.key === "Enter" &&
+                        !isTouchscreenDevice
+                    ) {
+                        e.preventDefault();
+                        save();
+                    }
+                }}
+                onKeyUp={onKeyUp}
+                onFocus={onFocus}
+                onBlur={onBlur}
+            />
+            <span className="caption">
+                escape to <a onClick={finish}>cancel</a> &middot; enter to{" "}
+                <a onClick={save}>save</a>
+            </span>
+        </EditorBase>
+    );
 }
diff --git a/src/pages/channels/messaging/MessageRenderer.tsx b/src/pages/channels/messaging/MessageRenderer.tsx
index 1a9ae45..9185685 100644
--- a/src/pages/channels/messaging/MessageRenderer.tsx
+++ b/src/pages/channels/messaging/MessageRenderer.tsx
@@ -26,190 +26,190 @@ import ConversationStart from "./ConversationStart";
 import MessageEditor from "./MessageEditor";
 
 interface Props {
-	id: string;
-	state: RenderState;
-	queue: QueuedMessage[];
+    id: string;
+    state: RenderState;
+    queue: QueuedMessage[];
 }
 
 const BlockedMessage = styled.div`
-	font-size: 0.8em;
-	margin-top: 6px;
-	padding: 4px 64px;
-	color: var(--tertiary-foreground);
-
-	&:hover {
-		background: var(--hover);
-	}
+    font-size: 0.8em;
+    margin-top: 6px;
+    padding: 4px 64px;
+    color: var(--tertiary-foreground);
+
+    &:hover {
+        background: var(--hover);
+    }
 `;
 
 function MessageRenderer({ id, state, queue }: Props) {
-	if (state.type !== "RENDER") return null;
-
-	const client = useContext(AppContext);
-	const userId = client.user!._id;
-
-	const [editing, setEditing] = useState<string | undefined>(undefined);
-	const stopEditing = () => {
-		setEditing(undefined);
-		internalEmit("TextArea", "focus", "message");
-	};
-
-	useEffect(() => {
-		function editLast() {
-			if (state.type !== "RENDER") return;
-			for (let i = state.messages.length - 1; i >= 0; i--) {
-				if (state.messages[i].author === userId) {
-					setEditing(state.messages[i]._id);
-					return;
-				}
-			}
-		}
-
-		const subs = [
-			internalSubscribe("MessageRenderer", "edit_last", editLast),
-			internalSubscribe("MessageRenderer", "edit_message", setEditing),
-		];
-
-		return () => subs.forEach((unsub) => unsub());
-	}, [state.messages]);
-
-	let render: Children[] = [],
-		previous: MessageObject | undefined;
-
-	if (state.atTop) {
-		render.push(<ConversationStart id={id} />);
-	} else {
-		render.push(
-			<RequiresOnline>
-				<Preloader type="ring" />
-			</RequiresOnline>,
-		);
-	}
-
-	let head = true;
-	function compare(
-		current: string,
-		curAuthor: string,
-		previous: string,
-		prevAuthor: string,
-	) {
-		const atime = decodeTime(current),
-			adate = new Date(atime),
-			btime = decodeTime(previous),
-			bdate = new Date(btime);
-
-		if (
-			adate.getFullYear() !== bdate.getFullYear() ||
-			adate.getMonth() !== bdate.getMonth() ||
-			adate.getDate() !== bdate.getDate()
-		) {
-			render.push(<DateDivider date={adate} />);
-			head = true;
-		}
-
-		head = curAuthor !== prevAuthor || Math.abs(btime - atime) >= 420000;
-	}
-
-	let blocked = 0;
-	function pushBlocked() {
-		render.push(
-			<BlockedMessage>
-				<X size={16} /> {blocked} blocked messages
-			</BlockedMessage>,
-		);
-		blocked = 0;
-	}
-
-	for (const message of state.messages) {
-		if (previous) {
-			compare(message._id, message.author, previous._id, previous.author);
-		}
-
-		if (message.author === "00000000000000000000000000") {
-			render.push(
-				<SystemMessage
-					key={message._id}
-					message={message}
-					attachContext
-				/>,
-			);
-		} else {
-			// ! FIXME: temp solution
-			if (
-				client.users.get(message.author)?.relationship ===
-				Users.Relationship.Blocked
-			) {
-				blocked++;
-			} else {
-				if (blocked > 0) pushBlocked();
-
-				render.push(
-					<Message
-						message={message}
-						key={message._id}
-						head={head}
-						content={
-							editing === message._id ? (
-								<MessageEditor
-									message={message}
-									finish={stopEditing}
-								/>
-							) : undefined
-						}
-						attachContext
-					/>,
-				);
-			}
-		}
-
-		previous = message;
-	}
-
-	if (blocked > 0) pushBlocked();
-
-	const nonces = state.messages.map((x) => x.nonce);
-	if (state.atBottom) {
-		for (const msg of queue) {
-			if (msg.channel !== id) continue;
-			if (nonces.includes(msg.id)) continue;
-
-			if (previous) {
-				compare(msg.id, userId!, previous._id, previous.author);
-
-				previous = {
-					_id: msg.id,
-					data: { author: userId! },
-				} as any;
-			}
-
-			render.push(
-				<Message
-					message={{
-						...msg.data,
-						replies: msg.data.replies.map((x) => x.id),
-					}}
-					key={msg.id}
-					queued={msg}
-					head={head}
-					attachContext
-				/>,
-			);
-		}
-	} else {
-		render.push(
-			<RequiresOnline>
-				<Preloader type="ring" />
-			</RequiresOnline>,
-		);
-	}
-
-	return <>{render}</>;
+    if (state.type !== "RENDER") return null;
+
+    const client = useContext(AppContext);
+    const userId = client.user!._id;
+
+    const [editing, setEditing] = useState<string | undefined>(undefined);
+    const stopEditing = () => {
+        setEditing(undefined);
+        internalEmit("TextArea", "focus", "message");
+    };
+
+    useEffect(() => {
+        function editLast() {
+            if (state.type !== "RENDER") return;
+            for (let i = state.messages.length - 1; i >= 0; i--) {
+                if (state.messages[i].author === userId) {
+                    setEditing(state.messages[i]._id);
+                    return;
+                }
+            }
+        }
+
+        const subs = [
+            internalSubscribe("MessageRenderer", "edit_last", editLast),
+            internalSubscribe("MessageRenderer", "edit_message", setEditing),
+        ];
+
+        return () => subs.forEach((unsub) => unsub());
+    }, [state.messages]);
+
+    let render: Children[] = [],
+        previous: MessageObject | undefined;
+
+    if (state.atTop) {
+        render.push(<ConversationStart id={id} />);
+    } else {
+        render.push(
+            <RequiresOnline>
+                <Preloader type="ring" />
+            </RequiresOnline>,
+        );
+    }
+
+    let head = true;
+    function compare(
+        current: string,
+        curAuthor: string,
+        previous: string,
+        prevAuthor: string,
+    ) {
+        const atime = decodeTime(current),
+            adate = new Date(atime),
+            btime = decodeTime(previous),
+            bdate = new Date(btime);
+
+        if (
+            adate.getFullYear() !== bdate.getFullYear() ||
+            adate.getMonth() !== bdate.getMonth() ||
+            adate.getDate() !== bdate.getDate()
+        ) {
+            render.push(<DateDivider date={adate} />);
+            head = true;
+        }
+
+        head = curAuthor !== prevAuthor || Math.abs(btime - atime) >= 420000;
+    }
+
+    let blocked = 0;
+    function pushBlocked() {
+        render.push(
+            <BlockedMessage>
+                <X size={16} /> {blocked} blocked messages
+            </BlockedMessage>,
+        );
+        blocked = 0;
+    }
+
+    for (const message of state.messages) {
+        if (previous) {
+            compare(message._id, message.author, previous._id, previous.author);
+        }
+
+        if (message.author === "00000000000000000000000000") {
+            render.push(
+                <SystemMessage
+                    key={message._id}
+                    message={message}
+                    attachContext
+                />,
+            );
+        } else {
+            // ! FIXME: temp solution
+            if (
+                client.users.get(message.author)?.relationship ===
+                Users.Relationship.Blocked
+            ) {
+                blocked++;
+            } else {
+                if (blocked > 0) pushBlocked();
+
+                render.push(
+                    <Message
+                        message={message}
+                        key={message._id}
+                        head={head}
+                        content={
+                            editing === message._id ? (
+                                <MessageEditor
+                                    message={message}
+                                    finish={stopEditing}
+                                />
+                            ) : undefined
+                        }
+                        attachContext
+                    />,
+                );
+            }
+        }
+
+        previous = message;
+    }
+
+    if (blocked > 0) pushBlocked();
+
+    const nonces = state.messages.map((x) => x.nonce);
+    if (state.atBottom) {
+        for (const msg of queue) {
+            if (msg.channel !== id) continue;
+            if (nonces.includes(msg.id)) continue;
+
+            if (previous) {
+                compare(msg.id, userId!, previous._id, previous.author);
+
+                previous = {
+                    _id: msg.id,
+                    data: { author: userId! },
+                } as any;
+            }
+
+            render.push(
+                <Message
+                    message={{
+                        ...msg.data,
+                        replies: msg.data.replies.map((x) => x.id),
+                    }}
+                    key={msg.id}
+                    queued={msg}
+                    head={head}
+                    attachContext
+                />,
+            );
+        }
+    } else {
+        render.push(
+            <RequiresOnline>
+                <Preloader type="ring" />
+            </RequiresOnline>,
+        );
+    }
+
+    return <>{render}</>;
 }
 
 export default memo(
-	connectState<Omit<Props, "queue">>(MessageRenderer, (state) => {
-		return {
-			queue: state.queue,
-		};
-	}),
+    connectState<Omit<Props, "queue">>(MessageRenderer, (state) => {
+        return {
+            queue: state.queue,
+        };
+    }),
 );
diff --git a/src/pages/channels/voice/VoiceHeader.tsx b/src/pages/channels/voice/VoiceHeader.tsx
index 106e27d..016391e 100644
--- a/src/pages/channels/voice/VoiceHeader.tsx
+++ b/src/pages/channels/voice/VoiceHeader.tsx
@@ -5,134 +5,134 @@ import { Text } from "preact-i18n";
 import { useContext } from "preact/hooks";
 
 import {
-	VoiceContext,
-	VoiceOperationsContext,
-	VoiceStatus,
+    VoiceContext,
+    VoiceOperationsContext,
+    VoiceStatus,
 } from "../../../context/Voice";
 import {
-	useForceUpdate,
-	useSelf,
-	useUsers,
+    useForceUpdate,
+    useSelf,
+    useUsers,
 } from "../../../context/revoltjs/hooks";
 
 import UserIcon from "../../../components/common/user/UserIcon";
 import Button from "../../../components/ui/Button";
 
 interface Props {
-	id: string;
+    id: string;
 }
 
 const VoiceBase = styled.div`
-	padding: 20px;
-	background: var(--secondary-background);
-
-	.status {
-		position: absolute;
-		color: var(--success);
-		background: var(--primary-background);
-		display: flex;
-		align-items: center;
-		padding: 10px;
-		font-size: 14px;
-		font-weight: 600;
-		border-radius: 7px;
-		flex: 1 0;
-		user-select: none;
-
-		svg {
-			margin-inline-end: 4px;
-			cursor: help;
-		}
-	}
-
-	display: flex;
-	flex-direction: column;
-
-	.participants {
-		margin: 20px 0;
-		justify-content: center;
-		pointer-events: none;
-		user-select: none;
-		display: flex;
-		gap: 16px;
-
-		.disconnected {
-			opacity: 0.5;
-		}
-	}
-
-	.actions {
-		display: flex;
-		justify-content: center;
-		gap: 10px;
-	}
+    padding: 20px;
+    background: var(--secondary-background);
+
+    .status {
+        position: absolute;
+        color: var(--success);
+        background: var(--primary-background);
+        display: flex;
+        align-items: center;
+        padding: 10px;
+        font-size: 14px;
+        font-weight: 600;
+        border-radius: 7px;
+        flex: 1 0;
+        user-select: none;
+
+        svg {
+            margin-inline-end: 4px;
+            cursor: help;
+        }
+    }
+
+    display: flex;
+    flex-direction: column;
+
+    .participants {
+        margin: 20px 0;
+        justify-content: center;
+        pointer-events: none;
+        user-select: none;
+        display: flex;
+        gap: 16px;
+
+        .disconnected {
+            opacity: 0.5;
+        }
+    }
+
+    .actions {
+        display: flex;
+        justify-content: center;
+        gap: 10px;
+    }
 `;
 
 export default function VoiceHeader({ id }: Props) {
-	const { status, participants, roomId } = useContext(VoiceContext);
-	if (roomId !== id) return null;
-
-	const { isProducing, startProducing, stopProducing, disconnect } =
-		useContext(VoiceOperationsContext);
-
-	const ctx = useForceUpdate();
-	const self = useSelf(ctx);
-	const keys = participants ? Array.from(participants.keys()) : undefined;
-	const users = keys ? useUsers(keys, ctx) : undefined;
-
-	return (
-		<VoiceBase>
-			<div className="participants">
-				{users && users.length !== 0
-					? users.map((user, index) => {
-							const id = keys![index];
-							return (
-								<div key={id}>
-									<UserIcon
-										size={80}
-										target={user}
-										status={false}
-										voice={
-											participants!.get(id)?.audio
-												? undefined
-												: "muted"
-										}
-									/>
-								</div>
-							);
-					  })
-					: self !== undefined && (
-							<div key={self._id} className="disconnected">
-								<UserIcon
-									size={80}
-									target={self}
-									status={false}
-								/>
-							</div>
-					  )}
-			</div>
-			<div className="status">
-				<BarChart size={20} />
-				{status === VoiceStatus.CONNECTED && (
-					<Text id="app.main.channel.voice.connected" />
-				)}
-			</div>
-			<div className="actions">
-				<Button error onClick={disconnect}>
-					<Text id="app.main.channel.voice.leave" />
-				</Button>
-				{isProducing("audio") ? (
-					<Button onClick={() => stopProducing("audio")}>
-						<Text id="app.main.channel.voice.mute" />
-					</Button>
-				) : (
-					<Button onClick={() => startProducing("audio")}>
-						<Text id="app.main.channel.voice.unmute" />
-					</Button>
-				)}
-			</div>
-		</VoiceBase>
-	);
+    const { status, participants, roomId } = useContext(VoiceContext);
+    if (roomId !== id) return null;
+
+    const { isProducing, startProducing, stopProducing, disconnect } =
+        useContext(VoiceOperationsContext);
+
+    const ctx = useForceUpdate();
+    const self = useSelf(ctx);
+    const keys = participants ? Array.from(participants.keys()) : undefined;
+    const users = keys ? useUsers(keys, ctx) : undefined;
+
+    return (
+        <VoiceBase>
+            <div className="participants">
+                {users && users.length !== 0
+                    ? users.map((user, index) => {
+                          const id = keys![index];
+                          return (
+                              <div key={id}>
+                                  <UserIcon
+                                      size={80}
+                                      target={user}
+                                      status={false}
+                                      voice={
+                                          participants!.get(id)?.audio
+                                              ? undefined
+                                              : "muted"
+                                      }
+                                  />
+                              </div>
+                          );
+                      })
+                    : self !== undefined && (
+                          <div key={self._id} className="disconnected">
+                              <UserIcon
+                                  size={80}
+                                  target={self}
+                                  status={false}
+                              />
+                          </div>
+                      )}
+            </div>
+            <div className="status">
+                <BarChart size={20} />
+                {status === VoiceStatus.CONNECTED && (
+                    <Text id="app.main.channel.voice.connected" />
+                )}
+            </div>
+            <div className="actions">
+                <Button error onClick={disconnect}>
+                    <Text id="app.main.channel.voice.leave" />
+                </Button>
+                {isProducing("audio") ? (
+                    <Button onClick={() => stopProducing("audio")}>
+                        <Text id="app.main.channel.voice.mute" />
+                    </Button>
+                ) : (
+                    <Button onClick={() => startProducing("audio")}>
+                        <Text id="app.main.channel.voice.unmute" />
+                    </Button>
+                )}
+            </div>
+        </VoiceBase>
+    );
 }
 
 /**{voice.roomId === id && (
diff --git a/src/pages/developer/Developer.tsx b/src/pages/developer/Developer.tsx
index 6e39a16..4b561d0 100644
--- a/src/pages/developer/Developer.tsx
+++ b/src/pages/developer/Developer.tsx
@@ -11,31 +11,31 @@ import { useUserPermission } from "../../context/revoltjs/hooks";
 import Header from "../../components/ui/Header";
 
 export default function Developer() {
-	// const voice = useContext(VoiceContext);
-	const client = useContext(AppContext);
-	const userPermission = useUserPermission(client.user!._id);
+    // const voice = useContext(VoiceContext);
+    const client = useContext(AppContext);
+    const userPermission = useUserPermission(client.user!._id);
 
-	return (
-		<div>
-			<Header placement="primary">
-				<Wrench size="24" />
-				Developer Tab
-			</Header>
-			<div style={{ padding: "16px" }}>
-				<PaintCounter always />
-			</div>
-			<div style={{ padding: "16px" }}>
-				<b>User ID:</b> {client.user!._id} <br />
-				<b>Permission against self:</b> {userPermission} <br />
-			</div>
-			<div style={{ padding: "16px" }}>
-				<TextReact
-					id="login.open_mail_provider"
-					fields={{ provider: <b>GAMING!</b> }}
-				/>
-			</div>
-			<div style={{ padding: "16px" }}>
-				{/*<span>
+    return (
+        <div>
+            <Header placement="primary">
+                <Wrench size="24" />
+                Developer Tab
+            </Header>
+            <div style={{ padding: "16px" }}>
+                <PaintCounter always />
+            </div>
+            <div style={{ padding: "16px" }}>
+                <b>User ID:</b> {client.user!._id} <br />
+                <b>Permission against self:</b> {userPermission} <br />
+            </div>
+            <div style={{ padding: "16px" }}>
+                <TextReact
+                    id="login.open_mail_provider"
+                    fields={{ provider: <b>GAMING!</b> }}
+                />
+            </div>
+            <div style={{ padding: "16px" }}>
+                {/*<span>
                     <b>Voice Status:</b> {VoiceStatus[voice.status]}
                 </span>
                 <br />
@@ -48,7 +48,7 @@ export default function Developer() {
                     {Array.from(voice.participants.keys()).join(", ")}]
                 </span>
                 <br />*/}
-			</div>
-		</div>
-	);
+            </div>
+        </div>
+    );
 }
diff --git a/src/pages/friends/Friend.tsx b/src/pages/friends/Friend.tsx
index 2632dcc..650ce70 100644
--- a/src/pages/friends/Friend.tsx
+++ b/src/pages/friends/Friend.tsx
@@ -13,8 +13,8 @@ import { stopPropagation } from "../../lib/stopPropagation";
 import { VoiceOperationsContext } from "../../context/Voice";
 import { useIntermediate } from "../../context/intermediate/Intermediate";
 import {
-	AppContext,
-	OperationsContext,
+    AppContext,
+    OperationsContext,
 } from "../../context/revoltjs/RevoltClient";
 
 import UserIcon from "../../components/common/user/UserIcon";
@@ -24,113 +24,113 @@ import IconButton from "../../components/ui/IconButton";
 import { Children } from "../../types/Preact";
 
 interface Props {
-	user: User;
+    user: User;
 }
 
 export function Friend({ user }: Props) {
-	const client = useContext(AppContext);
-	const { openScreen } = useIntermediate();
-	const { openDM } = useContext(OperationsContext);
-	const { connect } = useContext(VoiceOperationsContext);
+    const client = useContext(AppContext);
+    const { openScreen } = useIntermediate();
+    const { openDM } = useContext(OperationsContext);
+    const { connect } = useContext(VoiceOperationsContext);
 
-	const actions: Children[] = [];
-	let subtext: Children = null;
+    const actions: Children[] = [];
+    let subtext: Children = null;
 
-	if (user.relationship === Users.Relationship.Friend) {
-		subtext = <UserStatus user={user} />;
-		actions.push(
-			<>
-				<IconButton
-					type="circle"
-					className={classNames(
-						styles.button,
-						styles.call,
-						styles.success,
-					)}
-					onClick={(ev) =>
-						stopPropagation(ev, openDM(user._id).then(connect))
-					}>
-					<PhoneCall size={20} />
-				</IconButton>
-				<IconButton
-					type="circle"
-					className={styles.button}
-					onClick={(ev) => stopPropagation(ev, openDM(user._id))}>
-					<Envelope size={20} />
-				</IconButton>
-			</>,
-		);
-	}
+    if (user.relationship === Users.Relationship.Friend) {
+        subtext = <UserStatus user={user} />;
+        actions.push(
+            <>
+                <IconButton
+                    type="circle"
+                    className={classNames(
+                        styles.button,
+                        styles.call,
+                        styles.success,
+                    )}
+                    onClick={(ev) =>
+                        stopPropagation(ev, openDM(user._id).then(connect))
+                    }>
+                    <PhoneCall size={20} />
+                </IconButton>
+                <IconButton
+                    type="circle"
+                    className={styles.button}
+                    onClick={(ev) => stopPropagation(ev, openDM(user._id))}>
+                    <Envelope size={20} />
+                </IconButton>
+            </>,
+        );
+    }
 
-	if (user.relationship === Users.Relationship.Incoming) {
-		actions.push(
-			<IconButton
-				type="circle"
-				className={styles.button}
-				onClick={(ev) =>
-					stopPropagation(ev, client.users.addFriend(user.username))
-				}>
-				<Plus size={24} />
-			</IconButton>,
-		);
+    if (user.relationship === Users.Relationship.Incoming) {
+        actions.push(
+            <IconButton
+                type="circle"
+                className={styles.button}
+                onClick={(ev) =>
+                    stopPropagation(ev, client.users.addFriend(user.username))
+                }>
+                <Plus size={24} />
+            </IconButton>,
+        );
 
-		subtext = <Text id="app.special.friends.incoming" />;
-	}
+        subtext = <Text id="app.special.friends.incoming" />;
+    }
 
-	if (user.relationship === Users.Relationship.Outgoing) {
-		subtext = <Text id="app.special.friends.outgoing" />;
-	}
+    if (user.relationship === Users.Relationship.Outgoing) {
+        subtext = <Text id="app.special.friends.outgoing" />;
+    }
 
-	if (
-		user.relationship === Users.Relationship.Friend ||
-		user.relationship === Users.Relationship.Outgoing ||
-		user.relationship === Users.Relationship.Incoming
-	) {
-		actions.push(
-			<IconButton
-				type="circle"
-				className={classNames(styles.button, styles.error)}
-				onClick={(ev) =>
-					stopPropagation(
-						ev,
-						user.relationship === Users.Relationship.Friend
-							? openScreen({
-									id: "special_prompt",
-									type: "unfriend_user",
-									target: user,
-							  })
-							: client.users.removeFriend(user._id),
-					)
-				}>
-				<X size={24} />
-			</IconButton>,
-		);
-	}
+    if (
+        user.relationship === Users.Relationship.Friend ||
+        user.relationship === Users.Relationship.Outgoing ||
+        user.relationship === Users.Relationship.Incoming
+    ) {
+        actions.push(
+            <IconButton
+                type="circle"
+                className={classNames(styles.button, styles.error)}
+                onClick={(ev) =>
+                    stopPropagation(
+                        ev,
+                        user.relationship === Users.Relationship.Friend
+                            ? openScreen({
+                                  id: "special_prompt",
+                                  type: "unfriend_user",
+                                  target: user,
+                              })
+                            : client.users.removeFriend(user._id),
+                    )
+                }>
+                <X size={24} />
+            </IconButton>,
+        );
+    }
 
-	if (user.relationship === Users.Relationship.Blocked) {
-		actions.push(
-			<IconButton
-				type="circle"
-				className={classNames(styles.button, styles.error)}
-				onClick={(ev) =>
-					stopPropagation(ev, client.users.unblockUser(user._id))
-				}>
-				<X size={24} />
-			</IconButton>,
-		);
-	}
+    if (user.relationship === Users.Relationship.Blocked) {
+        actions.push(
+            <IconButton
+                type="circle"
+                className={classNames(styles.button, styles.error)}
+                onClick={(ev) =>
+                    stopPropagation(ev, client.users.unblockUser(user._id))
+                }>
+                <X size={24} />
+            </IconButton>,
+        );
+    }
 
-	return (
-		<div
-			className={styles.friend}
-			onClick={() => openScreen({ id: "profile", user_id: user._id })}
-			onContextMenu={attachContextMenu("Menu", { user: user._id })}>
-			<UserIcon target={user} size={36} status />
-			<div className={styles.name}>
-				<span>@{user.username}</span>
-				{subtext && <span className={styles.subtext}>{subtext}</span>}
-			</div>
-			<div className={styles.actions}>{actions}</div>
-		</div>
-	);
+    return (
+        <div
+            className={styles.friend}
+            onClick={() => openScreen({ id: "profile", user_id: user._id })}
+            onContextMenu={attachContextMenu("Menu", { user: user._id })}>
+            <UserIcon target={user} size={36} status />
+            <div className={styles.name}>
+                <span>@{user.username}</span>
+                {subtext && <span className={styles.subtext}>{subtext}</span>}
+            </div>
+            <div className={styles.actions}>{actions}</div>
+        </div>
+    );
 }
diff --git a/src/pages/friends/Friends.tsx b/src/pages/friends/Friends.tsx
index 6b322e5..d87170e 100644
--- a/src/pages/friends/Friends.tsx
+++ b/src/pages/friends/Friends.tsx
@@ -1,7 +1,7 @@
 import {
-	ChevronDown,
-	ChevronRight,
-	ListPlus,
+    ChevronDown,
+    ChevronRight,
+    ListPlus,
 } from "@styled-icons/boxicons-regular";
 import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid";
 import { User, Users } from "revolt.js/dist/api/objects";
@@ -27,91 +27,91 @@ import { Children } from "../../types/Preact";
 import { Friend } from "./Friend";
 
 export default function Friends() {
-	const { openScreen } = useIntermediate();
+    const { openScreen } = useIntermediate();
 
-	const users = useUsers() as User[];
-	users.sort((a, b) => a.username.localeCompare(b.username));
+    const users = useUsers() as User[];
+    users.sort((a, b) => a.username.localeCompare(b.username));
 
-	const friends = users.filter(
-		(x) => x.relationship === Users.Relationship.Friend,
-	);
+    const friends = users.filter(
+        (x) => x.relationship === Users.Relationship.Friend,
+    );
 
-	const lists = [
-		[
-			"",
-			users.filter((x) => x.relationship === Users.Relationship.Incoming),
-		],
-		[
-			"app.special.friends.sent",
-			users.filter((x) => x.relationship === Users.Relationship.Outgoing),
-			"outgoing",
-		],
-		[
-			"app.status.online",
-			friends.filter(
-				(x) =>
-					x.online && x.status?.presence !== Users.Presence.Invisible,
-			),
-			"online",
-		],
-		[
-			"app.status.offline",
-			friends.filter(
-				(x) =>
-					!x.online ||
-					x.status?.presence === Users.Presence.Invisible,
-			),
-			"offline",
-		],
-		[
-			"app.special.friends.blocked",
-			users.filter((x) => x.relationship === Users.Relationship.Blocked),
-			"blocked",
-		],
-	] as [string, User[], string][];
+    const lists = [
+        [
+            "",
+            users.filter((x) => x.relationship === Users.Relationship.Incoming),
+        ],
+        [
+            "app.special.friends.sent",
+            users.filter((x) => x.relationship === Users.Relationship.Outgoing),
+            "outgoing",
+        ],
+        [
+            "app.status.online",
+            friends.filter(
+                (x) =>
+                    x.online && x.status?.presence !== Users.Presence.Invisible,
+            ),
+            "online",
+        ],
+        [
+            "app.status.offline",
+            friends.filter(
+                (x) =>
+                    !x.online ||
+                    x.status?.presence === Users.Presence.Invisible,
+            ),
+            "offline",
+        ],
+        [
+            "app.special.friends.blocked",
+            users.filter((x) => x.relationship === Users.Relationship.Blocked),
+            "blocked",
+        ],
+    ] as [string, User[], string][];
 
-	const incoming = lists[0][1];
-	const userlist: Children[] = incoming.map((x) => <b>{x.username}</b>);
-	for (let i = incoming.length - 1; i > 0; i--) userlist.splice(i, 0, ", ");
+    const incoming = lists[0][1];
+    const userlist: Children[] = incoming.map((x) => <b>{x.username}</b>);
+    for (let i = incoming.length - 1; i > 0; i--) userlist.splice(i, 0, ", ");
 
-	const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
-	return (
-		<>
-			<Header placement="primary">
-				{!isTouchscreenDevice && <UserDetail size={24} />}
-				<div className={styles.title}>
-					<Text id="app.navigation.tabs.friends" />
-				</div>
-				<div className={styles.actions}>
-					{/*<Tooltip content={"Create Category"} placement="bottom">
+    const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
+    return (
+        <>
+            <Header placement="primary">
+                {!isTouchscreenDevice && <UserDetail size={24} />}
+                <div className={styles.title}>
+                    <Text id="app.navigation.tabs.friends" />
+                </div>
+                <div className={styles.actions}>
+                    {/*<Tooltip content={"Create Category"} placement="bottom">
                         <IconButton onClick={() => openScreen({ id: 'special_input', type: 'create_group' })}>
                             <ListPlus size={28} />
                         </IconButton>
                     </Tooltip>
                     <div className={styles.divider} />*/}
-					<Tooltip content={"Create Group"} placement="bottom">
-						<IconButton
-							onClick={() =>
-								openScreen({
-									id: "special_input",
-									type: "create_group",
-								})
-							}>
-							<MessageAdd size={24} />
-						</IconButton>
-					</Tooltip>
-					<Tooltip content={"Add Friend"} placement="bottom">
-						<IconButton
-							onClick={() =>
-								openScreen({
-									id: "special_input",
-									type: "add_friend",
-								})
-							}>
-							<UserPlus size={27} />
-						</IconButton>
-					</Tooltip>
-					{/* 
+                    <Tooltip content={"Create Group"} placement="bottom">
+                        <IconButton
+                            onClick={() =>
+                                openScreen({
+                                    id: "special_input",
+                                    type: "create_group",
+                                })
+                            }>
+                            <MessageAdd size={24} />
+                        </IconButton>
+                    </Tooltip>
+                    <Tooltip content={"Add Friend"} placement="bottom">
+                        <IconButton
+                            onClick={() =>
+                                openScreen({
+                                    id: "special_input",
+                                    type: "add_friend",
+                                })
+                            }>
+                            <UserPlus size={27} />
+                        </IconButton>
+                    </Tooltip>
+                    {/* 
                     <div className={styles.divider} />
                     <Tooltip content={"Friend Activity"} placement="bottom">
                         <IconButton>
@@ -119,98 +119,98 @@ export default function Friends() {
                         </IconButton>            
                     </Tooltip>
                     */}
-				</div>
-			</Header>
-			<div className={styles.list} data-empty={isEmpty}>
-				{isEmpty && (
-					<>
-						<img src="https://img.insrt.uk/xexu7/XOPoBUTI47.png/raw" />
-						<Text id="app.special.friends.nobody" />
-					</>
-				)}
+                </div>
+            </Header>
+            <div className={styles.list} data-empty={isEmpty}>
+                {isEmpty && (
+                    <>
+                        <img src="https://img.insrt.uk/xexu7/XOPoBUTI47.png/raw" />
+                        <Text id="app.special.friends.nobody" />
+                    </>
+                )}
 
-				{incoming.length > 0 && (
-					<div
-						className={styles.pending}
-						onClick={() =>
-							openScreen({
-								id: "pending_requests",
-								users: incoming.map((x) => x._id),
-							})
-						}>
-						<div className={styles.avatars}>
-							{incoming.map(
-								(x, i) =>
-									i < 3 && (
-										<UserIcon
-											target={x}
-											size={64}
-											mask={
-												i <
-												Math.min(incoming.length - 1, 2)
-													? "url(#overlap)"
-													: undefined
-											}
-										/>
-									),
-							)}
-						</div>
-						<div className={styles.details}>
-							<div>
-								<Text id="app.special.friends.pending" />{" "}
-								<span>{incoming.length}</span>
-							</div>
-							<span>
-								{incoming.length > 3 ? (
-									<TextReact
-										id="app.special.friends.from.several"
-										fields={{
-											userlist: userlist.slice(0, 6),
-											count: incoming.length - 3,
-										}}
-									/>
-								) : incoming.length > 1 ? (
-									<TextReact
-										id="app.special.friends.from.multiple"
-										fields={{
-											user: userlist.shift()!,
-											userlist: userlist.slice(1),
-										}}
-									/>
-								) : (
-									<TextReact
-										id="app.special.friends.from.single"
-										fields={{ user: userlist[0] }}
-									/>
-								)}
-							</span>
-						</div>
-						<ChevronRight size={28} />
-					</div>
-				)}
+                {incoming.length > 0 && (
+                    <div
+                        className={styles.pending}
+                        onClick={() =>
+                            openScreen({
+                                id: "pending_requests",
+                                users: incoming.map((x) => x._id),
+                            })
+                        }>
+                        <div className={styles.avatars}>
+                            {incoming.map(
+                                (x, i) =>
+                                    i < 3 && (
+                                        <UserIcon
+                                            target={x}
+                                            size={64}
+                                            mask={
+                                                i <
+                                                Math.min(incoming.length - 1, 2)
+                                                    ? "url(#overlap)"
+                                                    : undefined
+                                            }
+                                        />
+                                    ),
+                            )}
+                        </div>
+                        <div className={styles.details}>
+                            <div>
+                                <Text id="app.special.friends.pending" />{" "}
+                                <span>{incoming.length}</span>
+                            </div>
+                            <span>
+                                {incoming.length > 3 ? (
+                                    <TextReact
+                                        id="app.special.friends.from.several"
+                                        fields={{
+                                            userlist: userlist.slice(0, 6),
+                                            count: incoming.length - 3,
+                                        }}
+                                    />
+                                ) : incoming.length > 1 ? (
+                                    <TextReact
+                                        id="app.special.friends.from.multiple"
+                                        fields={{
+                                            user: userlist.shift()!,
+                                            userlist: userlist.slice(1),
+                                        }}
+                                    />
+                                ) : (
+                                    <TextReact
+                                        id="app.special.friends.from.single"
+                                        fields={{ user: userlist[0] }}
+                                    />
+                                )}
+                            </span>
+                        </div>
+                        <ChevronRight size={28} />
+                    </div>
+                )}
 
-				{lists.map(([i18n, list, section_id], index) => {
-					if (index === 0) return;
-					if (list.length === 0) return;
+                {lists.map(([i18n, list, section_id], index) => {
+                    if (index === 0) return;
+                    if (list.length === 0) return;
 
-					return (
-						<CollapsibleSection
-							id={`friends_${section_id}`}
-							defaultValue={true}
-							sticky
-							large
-							summary={
-								<div class="title">
-									<Text id={i18n} /> — {list.length}
-								</div>
-							}>
-							{list.map((x) => (
-								<Friend key={x._id} user={x} />
-							))}
-						</CollapsibleSection>
-					);
-				})}
-			</div>
-		</>
-	);
+                    return (
+                        <CollapsibleSection
+                            id={`friends_${section_id}`}
+                            defaultValue={true}
+                            sticky
+                            large
+                            summary={
+                                <div class="title">
+                                    <Text id={i18n} /> — {list.length}
+                                </div>
+                            }>
+                            {list.map((x) => (
+                                <Friend key={x._id} user={x} />
+                            ))}
+                        </CollapsibleSection>
+                    );
+                })}
+            </div>
+        </>
+    );
 }
diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx
index 50f3c49..a817fd3 100644
--- a/src/pages/home/Home.tsx
+++ b/src/pages/home/Home.tsx
@@ -8,34 +8,34 @@ import wideSVG from "../../assets/wide.svg";
 import Header from "../../components/ui/Header";
 
 export default function Home() {
-	return (
-		<div className={styles.home}>
-			<Header placement="primary">
-				<HomeIcon size={24} />
-				<Text id="app.navigation.tabs.home" />
-			</Header>
-			<h3>
-				<Text id="app.special.modals.onboarding.welcome" />{" "}
-				<img src={wideSVG} />
-			</h3>
-			<ul>
-				<li>
-					Go to your <Link to="/friends">friends list</Link>.
-				</li>
-				<li>
-					Give <Link to="/settings/feedback">feedback</Link>.
-				</li>
-				<li>
-					Join <Link to="/invite/Testers">testers server</Link>.
-				</li>
-				<li>
-					View{" "}
-					<a href="https://gitlab.insrt.uk/revolt" target="_blank">
-						source code
-					</a>
-					.
-				</li>
-			</ul>
-		</div>
-	);
+    return (
+        <div className={styles.home}>
+            <Header placement="primary">
+                <HomeIcon size={24} />
+                <Text id="app.navigation.tabs.home" />
+            </Header>
+            <h3>
+                <Text id="app.special.modals.onboarding.welcome" />{" "}
+                <img src={wideSVG} />
+            </h3>
+            <ul>
+                <li>
+                    Go to your <Link to="/friends">friends list</Link>.
+                </li>
+                <li>
+                    Give <Link to="/settings/feedback">feedback</Link>.
+                </li>
+                <li>
+                    Join <Link to="/invite/Testers">testers server</Link>.
+                </li>
+                <li>
+                    View{" "}
+                    <a href="https://gitlab.insrt.uk/revolt" target="_blank">
+                        source code
+                    </a>
+                    .
+                </li>
+            </ul>
+        </div>
+    );
 }
diff --git a/src/pages/invite/Invite.tsx b/src/pages/invite/Invite.tsx
index eae15f0..16b89bf 100644
--- a/src/pages/invite/Invite.tsx
+++ b/src/pages/invite/Invite.tsx
@@ -7,9 +7,9 @@ import { useContext, useEffect, useState } from "preact/hooks";
 
 import RequiresOnline from "../../context/revoltjs/RequiresOnline";
 import {
-	AppContext,
-	ClientStatus,
-	StatusContext,
+    AppContext,
+    ClientStatus,
+    StatusContext,
 } from "../../context/revoltjs/RevoltClient";
 import { takeError } from "../../context/revoltjs/util";
 
@@ -20,109 +20,109 @@ import Overline from "../../components/ui/Overline";
 import Preloader from "../../components/ui/Preloader";
 
 export default function Invite() {
-	const history = useHistory();
-	const client = useContext(AppContext);
-	const status = useContext(StatusContext);
-	const { code } = useParams<{ code: string }>();
-	const [processing, setProcessing] = useState(false);
-	const [error, setError] = useState<string | undefined>(undefined);
-	const [invite, setInvite] = useState<Invites.RetrievedInvite | undefined>(
-		undefined,
-	);
+    const history = useHistory();
+    const client = useContext(AppContext);
+    const status = useContext(StatusContext);
+    const { code } = useParams<{ code: string }>();
+    const [processing, setProcessing] = useState(false);
+    const [error, setError] = useState<string | undefined>(undefined);
+    const [invite, setInvite] = useState<Invites.RetrievedInvite | undefined>(
+        undefined,
+    );
 
-	useEffect(() => {
-		if (
-			typeof invite === "undefined" &&
-			(status === ClientStatus.ONLINE || status === ClientStatus.READY)
-		) {
-			client
-				.fetchInvite(code)
-				.then((data) => setInvite(data))
-				.catch((err) => setError(takeError(err)));
-		}
-	}, [status]);
+    useEffect(() => {
+        if (
+            typeof invite === "undefined" &&
+            (status === ClientStatus.ONLINE || status === ClientStatus.READY)
+        ) {
+            client
+                .fetchInvite(code)
+                .then((data) => setInvite(data))
+                .catch((err) => setError(takeError(err)));
+        }
+    }, [status]);
 
-	if (typeof invite === "undefined") {
-		return (
-			<div className={styles.preloader}>
-				<RequiresOnline>
-					{error ? (
-						<Overline type="error" error={error} />
-					) : (
-						<Preloader type="spinner" />
-					)}
-				</RequiresOnline>
-			</div>
-		);
-	}
+    if (typeof invite === "undefined") {
+        return (
+            <div className={styles.preloader}>
+                <RequiresOnline>
+                    {error ? (
+                        <Overline type="error" error={error} />
+                    ) : (
+                        <Preloader type="spinner" />
+                    )}
+                </RequiresOnline>
+            </div>
+        );
+    }
 
-	// ! FIXME: add i18n translations
-	return (
-		<div
-			className={styles.invite}
-			style={{
-				backgroundImage: invite.server_banner
-					? `url('${client.generateFileURL(invite.server_banner)}')`
-					: undefined,
-			}}>
-			<div className={styles.leave}>
-				<ArrowBack size={32} onClick={() => history.push("/")} />
-			</div>
+    // ! FIXME: add i18n translations
+    return (
+        <div
+            className={styles.invite}
+            style={{
+                backgroundImage: invite.server_banner
+                    ? `url('${client.generateFileURL(invite.server_banner)}')`
+                    : undefined,
+            }}>
+            <div className={styles.leave}>
+                <ArrowBack size={32} onClick={() => history.push("/")} />
+            </div>
 
-			{!processing && (
-				<div className={styles.icon}>
-					<ServerIcon
-						attachment={invite.server_icon}
-						server_name={invite.server_name}
-						size={64}
-					/>
-				</div>
-			)}
+            {!processing && (
+                <div className={styles.icon}>
+                    <ServerIcon
+                        attachment={invite.server_icon}
+                        server_name={invite.server_name}
+                        size={64}
+                    />
+                </div>
+            )}
 
-			<div className={styles.details}>
-				{processing ? (
-					<Preloader type="ring" />
-				) : (
-					<>
-						<h1>{invite.server_name}</h1>
-						<h2>#{invite.channel_name}</h2>
-						<h3>
-							Invited by{" "}
-							<UserIcon
-								size={24}
-								attachment={invite.user_avatar}
-							/>{" "}
-							{invite.user_name}
-						</h3>
-						<Overline type="error" error={error} />
-						<Button
-							contrast
-							onClick={async () => {
-								if (status === ClientStatus.READY) {
-									return history.push("/");
-								}
+            <div className={styles.details}>
+                {processing ? (
+                    <Preloader type="ring" />
+                ) : (
+                    <>
+                        <h1>{invite.server_name}</h1>
+                        <h2>#{invite.channel_name}</h2>
+                        <h3>
+                            Invited by{" "}
+                            <UserIcon
+                                size={24}
+                                attachment={invite.user_avatar}
+                            />{" "}
+                            {invite.user_name}
+                        </h3>
+                        <Overline type="error" error={error} />
+                        <Button
+                            contrast
+                            onClick={async () => {
+                                if (status === ClientStatus.READY) {
+                                    return history.push("/");
+                                }
 
-								try {
-									setProcessing(true);
+                                try {
+                                    setProcessing(true);
 
-									let result = await client.joinInvite(code);
-									if (result.type === "Server") {
-										history.push(
-											`/server/${result.server._id}/channel/${result.channel._id}`,
-										);
-									}
-								} catch (err) {
-									setError(takeError(err));
-									setProcessing(false);
-								}
-							}}>
-							{status === ClientStatus.READY
-								? "Login to REVOLT"
-								: "Accept Invite"}
-						</Button>
-					</>
-				)}
-			</div>
-		</div>
-	);
+                                    let result = await client.joinInvite(code);
+                                    if (result.type === "Server") {
+                                        history.push(
+                                            `/server/${result.server._id}/channel/${result.channel._id}`,
+                                        );
+                                    }
+                                } catch (err) {
+                                    setError(takeError(err));
+                                    setProcessing(false);
+                                }
+                            }}>
+                            {status === ClientStatus.READY
+                                ? "Login to REVOLT"
+                                : "Accept Invite"}
+                        </Button>
+                    </>
+                )}
+            </div>
+        </div>
+    );
 }
diff --git a/src/pages/login/FormField.tsx b/src/pages/login/FormField.tsx
index 059e6be..54b0d38 100644
--- a/src/pages/login/FormField.tsx
+++ b/src/pages/login/FormField.tsx
@@ -4,68 +4,68 @@ import InputBox from "../../components/ui/InputBox";
 import Overline from "../../components/ui/Overline";
 
 interface Props {
-	type: "email" | "username" | "password" | "invite" | "current_password";
-	showOverline?: boolean;
-	register: Function;
-	error?: string;
-	name?: string;
+    type: "email" | "username" | "password" | "invite" | "current_password";
+    showOverline?: boolean;
+    register: Function;
+    error?: string;
+    name?: string;
 }
 
 export default function FormField({
-	type,
-	register,
-	showOverline,
-	error,
-	name,
+    type,
+    register,
+    showOverline,
+    error,
+    name,
 }: Props) {
-	return (
-		<>
-			{showOverline && (
-				<Overline error={error}>
-					<Text id={`login.${type}`} />
-				</Overline>
-			)}
-			<Localizer>
-				<InputBox
-					// Styled uses React typing while we use Preact
-					// this leads to inconsistances where things need to be typed oddly
-					placeholder={(<Text id={`login.enter.${type}`} />) as any}
-					name={
-						type === "current_password" ? "password" : name ?? type
-					}
-					type={
-						type === "invite" || type === "username"
-							? "text"
-							: type === "current_password"
-							? "password"
-							: type
-					}
-					ref={register(
-						type === "password" || type === "current_password"
-							? {
-									validate: (value: string) =>
-										value.length === 0
-											? "RequiredField"
-											: value.length < 8
-											? "TooShort"
-											: value.length > 1024
-											? "TooLong"
-											: undefined,
-							  }
-							: type === "email"
-							? {
-									required: "RequiredField",
-									pattern: {
-										value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
-										message: "InvalidEmail",
-									},
-							  }
-							: type === "username"
-							? { required: "RequiredField" }
-							: { required: "RequiredField" },
-					)}
-				/>
-			</Localizer>
-		</>
-	);
+    return (
+        <>
+            {showOverline && (
+                <Overline error={error}>
+                    <Text id={`login.${type}`} />
+                </Overline>
+            )}
+            <Localizer>
+                <InputBox
+                    // Styled uses React typing while we use Preact
+                    // this leads to inconsistances where things need to be typed oddly
+                    placeholder={(<Text id={`login.enter.${type}`} />) as any}
+                    name={
+                        type === "current_password" ? "password" : name ?? type
+                    }
+                    type={
+                        type === "invite" || type === "username"
+                            ? "text"
+                            : type === "current_password"
+                            ? "password"
+                            : type
+                    }
+                    ref={register(
+                        type === "password" || type === "current_password"
+                            ? {
+                                  validate: (value: string) =>
+                                      value.length === 0
+                                          ? "RequiredField"
+                                          : value.length < 8
+                                          ? "TooShort"
+                                          : value.length > 1024
+                                          ? "TooLong"
+                                          : undefined,
+                              }
+                            : type === "email"
+                            ? {
+                                  required: "RequiredField",
+                                  pattern: {
+                                      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
+                                      message: "InvalidEmail",
+                                  },
+                              }
+                            : type === "username"
+                            ? { required: "RequiredField" }
+                            : { required: "RequiredField" },
+                    )}
+                />
+            </Localizer>
+        </>
+    );
 }
diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx
index ffc2e2c..9e3d6f4 100644
--- a/src/pages/login/Login.tsx
+++ b/src/pages/login/Login.tsx
@@ -19,56 +19,56 @@ import { FormResend } from "./forms/FormResend";
 import { FormReset, FormSendReset } from "./forms/FormReset";
 
 export default function Login() {
-	const theme = useContext(ThemeContext);
-	const client = useContext(AppContext);
+    const theme = useContext(ThemeContext);
+    const client = useContext(AppContext);
 
-	return (
-		<div className={styles.login}>
-			<Helmet>
-				<meta name="theme-color" content={theme.background} />
-			</Helmet>
-			<div className={styles.content}>
-				<div className={styles.attribution}>
-					<span>
-						API:{" "}
-						<code>{client.configuration?.revolt ?? "???"}</code>{" "}
-						&middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "}
-						&middot; App: <code>{APP_VERSION}</code>
-					</span>
-					<span>
-						<LocaleSelector />
-					</span>
-				</div>
-				<div className={styles.modal}>
-					<Switch>
-						<Route path="/login/create">
-							<FormCreate />
-						</Route>
-						<Route path="/login/resend">
-							<FormResend />
-						</Route>
-						<Route path="/login/reset/:token">
-							<FormReset />
-						</Route>
-						<Route path="/login/reset">
-							<FormSendReset />
-						</Route>
-						<Route path="/">
-							<FormLogin />
-						</Route>
-					</Switch>
-				</div>
-				<div className={styles.attribution}>
-					<span>
-						<Text id="general.image_by" /> &lrm;@lorenzoherrera
-						&rlm;· unsplash.com
-					</span>
-				</div>
-			</div>
-			<div
-				className={styles.bg}
-				style={{ background: `url('${background}')` }}
-			/>
-		</div>
-	);
+    return (
+        <div className={styles.login}>
+            <Helmet>
+                <meta name="theme-color" content={theme.background} />
+            </Helmet>
+            <div className={styles.content}>
+                <div className={styles.attribution}>
+                    <span>
+                        API:{" "}
+                        <code>{client.configuration?.revolt ?? "???"}</code>{" "}
+                        &middot; revolt.js: <code>{LIBRARY_VERSION}</code>{" "}
+                        &middot; App: <code>{APP_VERSION}</code>
+                    </span>
+                    <span>
+                        <LocaleSelector />
+                    </span>
+                </div>
+                <div className={styles.modal}>
+                    <Switch>
+                        <Route path="/login/create">
+                            <FormCreate />
+                        </Route>
+                        <Route path="/login/resend">
+                            <FormResend />
+                        </Route>
+                        <Route path="/login/reset/:token">
+                            <FormReset />
+                        </Route>
+                        <Route path="/login/reset">
+                            <FormSendReset />
+                        </Route>
+                        <Route path="/">
+                            <FormLogin />
+                        </Route>
+                    </Switch>
+                </div>
+                <div className={styles.attribution}>
+                    <span>
+                        <Text id="general.image_by" /> &lrm;@lorenzoherrera
+                        &rlm;· unsplash.com
+                    </span>
+                </div>
+            </div>
+            <div
+                className={styles.bg}
+                style={{ background: `url('${background}')` }}
+            />
+        </div>
+    );
 }
diff --git a/src/pages/login/forms/CaptchaBlock.tsx b/src/pages/login/forms/CaptchaBlock.tsx
index 9cca755..af62e01 100644
--- a/src/pages/login/forms/CaptchaBlock.tsx
+++ b/src/pages/login/forms/CaptchaBlock.tsx
@@ -9,33 +9,33 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import Preloader from "../../../components/ui/Preloader";
 
 export interface CaptchaProps {
-	onSuccess: (token?: string) => void;
-	onCancel: () => void;
+    onSuccess: (token?: string) => void;
+    onCancel: () => void;
 }
 
 export function CaptchaBlock(props: CaptchaProps) {
-	const client = useContext(AppContext);
-
-	useEffect(() => {
-		if (!client.configuration?.features.captcha.enabled) {
-			props.onSuccess();
-		}
-	}, []);
-
-	if (!client.configuration?.features.captcha.enabled)
-		return <Preloader type="spinner" />;
-
-	return (
-		<div>
-			<HCaptcha
-				sitekey={client.configuration.features.captcha.key}
-				onVerify={(token) => props.onSuccess(token)}
-			/>
-			<div className={styles.footer}>
-				<a onClick={props.onCancel}>
-					<Text id="login.cancel" />
-				</a>
-			</div>
-		</div>
-	);
+    const client = useContext(AppContext);
+
+    useEffect(() => {
+        if (!client.configuration?.features.captcha.enabled) {
+            props.onSuccess();
+        }
+    }, []);
+
+    if (!client.configuration?.features.captcha.enabled)
+        return <Preloader type="spinner" />;
+
+    return (
+        <div>
+            <HCaptcha
+                sitekey={client.configuration.features.captcha.key}
+                onVerify={(token) => props.onSuccess(token)}
+            />
+            <div className={styles.footer}>
+                <a onClick={props.onCancel}>
+                    <Text id="login.cancel" />
+                </a>
+            </div>
+        </div>
+    );
 }
diff --git a/src/pages/login/forms/Form.tsx b/src/pages/login/forms/Form.tsx
index 4caf7b9..8cfb8b0 100644
--- a/src/pages/login/forms/Form.tsx
+++ b/src/pages/login/forms/Form.tsx
@@ -20,232 +20,232 @@ import { Legal } from "./Legal";
 import { MailProvider } from "./MailProvider";
 
 interface Props {
-	page: "create" | "login" | "send_reset" | "reset" | "resend";
-	callback: (fields: {
-		email: string;
-		password: string;
-		invite: string;
-		captcha?: string;
-	}) => Promise<void>;
+    page: "create" | "login" | "send_reset" | "reset" | "resend";
+    callback: (fields: {
+        email: string;
+        password: string;
+        invite: string;
+        captcha?: string;
+    }) => Promise<void>;
 }
 
 function getInviteCode() {
-	if (typeof window === "undefined") return "";
+    if (typeof window === "undefined") return "";
 
-	const urlParams = new URLSearchParams(window.location.search);
-	const code = urlParams.get("code");
-	return code ?? "";
+    const urlParams = new URLSearchParams(window.location.search);
+    const code = urlParams.get("code");
+    return code ?? "";
 }
 
 interface FormInputs {
-	email: string;
-	password: string;
-	invite: string;
+    email: string;
+    password: string;
+    invite: string;
 }
 
 export function Form({ page, callback }: Props) {
-	const client = useContext(AppContext);
-
-	const [loading, setLoading] = useState(false);
-	const [success, setSuccess] = useState<string | undefined>(undefined);
-	const [error, setGlobalError] = useState<string | undefined>(undefined);
-	const [captcha, setCaptcha] = useState<CaptchaProps | undefined>(undefined);
-
-	const { handleSubmit, register, errors, setError } = useForm<FormInputs>({
-		defaultValues: {
-			email: "",
-			password: "",
-			invite: getInviteCode(),
-		},
-	});
-
-	async function onSubmit(data: FormInputs) {
-		setGlobalError(undefined);
-		setLoading(true);
-
-		function onError(err: any) {
-			setLoading(false);
-
-			const error = takeError(err);
-			switch (error) {
-				case "email_in_use":
-					return setError("email", { type: "", message: error });
-				case "unknown_user":
-					return setError("email", { type: "", message: error });
-				case "invalid_invite":
-					return setError("invite", { type: "", message: error });
-			}
-
-			setGlobalError(error);
-		}
-
-		try {
-			if (
-				client.configuration?.features.captcha.enabled &&
-				page !== "reset"
-			) {
-				setCaptcha({
-					onSuccess: async (captcha) => {
-						setCaptcha(undefined);
-						try {
-							await callback({ ...data, captcha });
-							setSuccess(data.email);
-						} catch (err) {
-							onError(err);
-						}
-					},
-					onCancel: () => {
-						setCaptcha(undefined);
-						setLoading(false);
-					},
-				});
-			} else {
-				await callback(data);
-				setSuccess(data.email);
-			}
-		} catch (err) {
-			onError(err);
-		}
-	}
-
-	if (typeof success !== "undefined") {
-		return (
-			<div className={styles.success}>
-				{client.configuration?.features.email ? (
-					<>
-						<Envelope size={72} />
-						<h2>
-							<Text id="login.check_mail" />
-						</h2>
-						<p className={styles.note}>
-							<Text id="login.email_delay" />
-						</p>
-						<MailProvider email={success} />
-					</>
-				) : (
-					<>
-						<CheckCircle size={72} />
-						<h1>
-							<Text id="login.successful_registration" />
-						</h1>
-					</>
-				)}
-				<span className={styles.footer}>
-					<Link to="/login">
-						<a>
-							<Text id="login.remembered" />
-						</a>
-					</Link>
-				</span>
-			</div>
-		);
-	}
-
-	if (captcha) return <CaptchaBlock {...captcha} />;
-	if (loading) return <Preloader type="spinner" />;
-
-	return (
-		<div className={styles.form}>
-			<img src={wideSVG} />
-			{/* Preact / React typing incompatabilities */}
-			<form
-				onSubmit={
-					handleSubmit(
-						onSubmit,
-					) as JSX.GenericEventHandler<HTMLFormElement>
-				}>
-				{page !== "reset" && (
-					<FormField
-						type="email"
-						register={register}
-						showOverline
-						error={errors.email?.message}
-					/>
-				)}
-				{(page === "login" ||
-					page === "create" ||
-					page === "reset") && (
-					<FormField
-						type="password"
-						register={register}
-						showOverline
-						error={errors.password?.message}
-					/>
-				)}
-				{client.configuration?.features.invite_only &&
-					page === "create" && (
-						<FormField
-							type="invite"
-							register={register}
-							showOverline
-							error={errors.invite?.message}
-						/>
-					)}
-				{error && (
-					<Overline type="error" error={error}>
-						<Text id={`login.error.${page}`} />
-					</Overline>
-				)}
-				<Button>
-					<Text
-						id={
-							page === "create"
-								? "login.register"
-								: page === "login"
-								? "login.title"
-								: page === "reset"
-								? "login.set_password"
-								: page === "resend"
-								? "login.resend"
-								: "login.reset"
-						}
-					/>
-				</Button>
-			</form>
-			{page === "create" && (
-				<>
-					<span className={styles.create}>
-						<Text id="login.existing" />
-						<Link to="/login">
-							<Text id="login.title" />
-						</Link>
-					</span>
-					<span className={styles.create}>
-						<Text id="login.missing_verification" />
-						<Link to="/login/resend">
-							<Text id="login.resend" />
-						</Link>
-					</span>
-				</>
-			)}
-			{page === "login" && (
-				<>
-					<span className={styles.create}>
-						<Text id="login.new" />
-						<Link to="/login/create">
-							<Text id="login.create" />
-						</Link>
-					</span>
-					<span className={styles.create}>
-						<Text id="login.forgot" />
-						<Link to="/login/reset">
-							<Text id="login.reset" />
-						</Link>
-					</span>
-				</>
-			)}
-			{(page === "reset" ||
-				page === "resend" ||
-				page === "send_reset") && (
-				<>
-					<span className={styles.create}>
-						<Link to="/login">
-							<Text id="login.remembered" />
-						</Link>
-					</span>
-				</>
-			)}
-			<Legal />
-		</div>
-	);
+    const client = useContext(AppContext);
+
+    const [loading, setLoading] = useState(false);
+    const [success, setSuccess] = useState<string | undefined>(undefined);
+    const [error, setGlobalError] = useState<string | undefined>(undefined);
+    const [captcha, setCaptcha] = useState<CaptchaProps | undefined>(undefined);
+
+    const { handleSubmit, register, errors, setError } = useForm<FormInputs>({
+        defaultValues: {
+            email: "",
+            password: "",
+            invite: getInviteCode(),
+        },
+    });
+
+    async function onSubmit(data: FormInputs) {
+        setGlobalError(undefined);
+        setLoading(true);
+
+        function onError(err: any) {
+            setLoading(false);
+
+            const error = takeError(err);
+            switch (error) {
+                case "email_in_use":
+                    return setError("email", { type: "", message: error });
+                case "unknown_user":
+                    return setError("email", { type: "", message: error });
+                case "invalid_invite":
+                    return setError("invite", { type: "", message: error });
+            }
+
+            setGlobalError(error);
+        }
+
+        try {
+            if (
+                client.configuration?.features.captcha.enabled &&
+                page !== "reset"
+            ) {
+                setCaptcha({
+                    onSuccess: async (captcha) => {
+                        setCaptcha(undefined);
+                        try {
+                            await callback({ ...data, captcha });
+                            setSuccess(data.email);
+                        } catch (err) {
+                            onError(err);
+                        }
+                    },
+                    onCancel: () => {
+                        setCaptcha(undefined);
+                        setLoading(false);
+                    },
+                });
+            } else {
+                await callback(data);
+                setSuccess(data.email);
+            }
+        } catch (err) {
+            onError(err);
+        }
+    }
+
+    if (typeof success !== "undefined") {
+        return (
+            <div className={styles.success}>
+                {client.configuration?.features.email ? (
+                    <>
+                        <Envelope size={72} />
+                        <h2>
+                            <Text id="login.check_mail" />
+                        </h2>
+                        <p className={styles.note}>
+                            <Text id="login.email_delay" />
+                        </p>
+                        <MailProvider email={success} />
+                    </>
+                ) : (
+                    <>
+                        <CheckCircle size={72} />
+                        <h1>
+                            <Text id="login.successful_registration" />
+                        </h1>
+                    </>
+                )}
+                <span className={styles.footer}>
+                    <Link to="/login">
+                        <a>
+                            <Text id="login.remembered" />
+                        </a>
+                    </Link>
+                </span>
+            </div>
+        );
+    }
+
+    if (captcha) return <CaptchaBlock {...captcha} />;
+    if (loading) return <Preloader type="spinner" />;
+
+    return (
+        <div className={styles.form}>
+            <img src={wideSVG} />
+            {/* Preact / React typing incompatabilities */}
+            <form
+                onSubmit={
+                    handleSubmit(
+                        onSubmit,
+                    ) as JSX.GenericEventHandler<HTMLFormElement>
+                }>
+                {page !== "reset" && (
+                    <FormField
+                        type="email"
+                        register={register}
+                        showOverline
+                        error={errors.email?.message}
+                    />
+                )}
+                {(page === "login" ||
+                    page === "create" ||
+                    page === "reset") && (
+                    <FormField
+                        type="password"
+                        register={register}
+                        showOverline
+                        error={errors.password?.message}
+                    />
+                )}
+                {client.configuration?.features.invite_only &&
+                    page === "create" && (
+                        <FormField
+                            type="invite"
+                            register={register}
+                            showOverline
+                            error={errors.invite?.message}
+                        />
+                    )}
+                {error && (
+                    <Overline type="error" error={error}>
+                        <Text id={`login.error.${page}`} />
+                    </Overline>
+                )}
+                <Button>
+                    <Text
+                        id={
+                            page === "create"
+                                ? "login.register"
+                                : page === "login"
+                                ? "login.title"
+                                : page === "reset"
+                                ? "login.set_password"
+                                : page === "resend"
+                                ? "login.resend"
+                                : "login.reset"
+                        }
+                    />
+                </Button>
+            </form>
+            {page === "create" && (
+                <>
+                    <span className={styles.create}>
+                        <Text id="login.existing" />
+                        <Link to="/login">
+                            <Text id="login.title" />
+                        </Link>
+                    </span>
+                    <span className={styles.create}>
+                        <Text id="login.missing_verification" />
+                        <Link to="/login/resend">
+                            <Text id="login.resend" />
+                        </Link>
+                    </span>
+                </>
+            )}
+            {page === "login" && (
+                <>
+                    <span className={styles.create}>
+                        <Text id="login.new" />
+                        <Link to="/login/create">
+                            <Text id="login.create" />
+                        </Link>
+                    </span>
+                    <span className={styles.create}>
+                        <Text id="login.forgot" />
+                        <Link to="/login/reset">
+                            <Text id="login.reset" />
+                        </Link>
+                    </span>
+                </>
+            )}
+            {(page === "reset" ||
+                page === "resend" ||
+                page === "send_reset") && (
+                <>
+                    <span className={styles.create}>
+                        <Link to="/login">
+                            <Text id="login.remembered" />
+                        </Link>
+                    </span>
+                </>
+            )}
+            <Legal />
+        </div>
+    );
 }
diff --git a/src/pages/login/forms/FormCreate.tsx b/src/pages/login/forms/FormCreate.tsx
index 2ea6e32..c141532 100644
--- a/src/pages/login/forms/FormCreate.tsx
+++ b/src/pages/login/forms/FormCreate.tsx
@@ -5,14 +5,14 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { Form } from "./Form";
 
 export function FormCreate() {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	return (
-		<Form
-			page="create"
-			callback={async (data) => {
-				await client.register(import.meta.env.VITE_API_URL, data);
-			}}
-		/>
-	);
+    return (
+        <Form
+            page="create"
+            callback={async (data) => {
+                await client.register(import.meta.env.VITE_API_URL, data);
+            }}
+        />
+    );
 }
diff --git a/src/pages/login/forms/FormLogin.tsx b/src/pages/login/forms/FormLogin.tsx
index b7ea421..a2c75d6 100644
--- a/src/pages/login/forms/FormLogin.tsx
+++ b/src/pages/login/forms/FormLogin.tsx
@@ -8,25 +8,25 @@ import { OperationsContext } from "../../../context/revoltjs/RevoltClient";
 import { Form } from "./Form";
 
 export function FormLogin() {
-	const { login } = useContext(OperationsContext);
-	const history = useHistory();
+    const { login } = useContext(OperationsContext);
+    const history = useHistory();
 
-	return (
-		<Form
-			page="login"
-			callback={async (data) => {
-				const browser = detect();
-				let device_name;
-				if (browser) {
-					const { name, os } = browser;
-					device_name = `${name} on ${os}`;
-				} else {
-					device_name = "Unknown Device";
-				}
+    return (
+        <Form
+            page="login"
+            callback={async (data) => {
+                const browser = detect();
+                let device_name;
+                if (browser) {
+                    const { name, os } = browser;
+                    device_name = `${name} on ${os}`;
+                } else {
+                    device_name = "Unknown Device";
+                }
 
-				await login({ ...data, device_name });
-				history.push("/");
-			}}
-		/>
-	);
+                await login({ ...data, device_name });
+                history.push("/");
+            }}
+        />
+    );
 }
diff --git a/src/pages/login/forms/FormResend.tsx b/src/pages/login/forms/FormResend.tsx
index da1e27e..bc2cefb 100644
--- a/src/pages/login/forms/FormResend.tsx
+++ b/src/pages/login/forms/FormResend.tsx
@@ -5,14 +5,14 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { Form } from "./Form";
 
 export function FormResend() {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	return (
-		<Form
-			page="resend"
-			callback={async (data) => {
-				await client.req("POST", "/auth/resend", data);
-			}}
-		/>
-	);
+    return (
+        <Form
+            page="resend"
+            callback={async (data) => {
+                await client.req("POST", "/auth/resend", data);
+            }}
+        />
+    );
 }
diff --git a/src/pages/login/forms/FormReset.tsx b/src/pages/login/forms/FormReset.tsx
index 8e26568..28b49e0 100644
--- a/src/pages/login/forms/FormReset.tsx
+++ b/src/pages/login/forms/FormReset.tsx
@@ -7,33 +7,33 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import { Form } from "./Form";
 
 export function FormSendReset() {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	return (
-		<Form
-			page="send_reset"
-			callback={async (data) => {
-				await client.req("POST", "/auth/send_reset", data);
-			}}
-		/>
-	);
+    return (
+        <Form
+            page="send_reset"
+            callback={async (data) => {
+                await client.req("POST", "/auth/send_reset", data);
+            }}
+        />
+    );
 }
 
 export function FormReset() {
-	const { token } = useParams<{ token: string }>();
-	const client = useContext(AppContext);
-	const history = useHistory();
+    const { token } = useParams<{ token: string }>();
+    const client = useContext(AppContext);
+    const history = useHistory();
 
-	return (
-		<Form
-			page="reset"
-			callback={async (data) => {
-				await client.req("POST", "/auth/reset", {
-					token,
-					...data,
-				});
-				history.push("/login");
-			}}
-		/>
-	);
+    return (
+        <Form
+            page="reset"
+            callback={async (data) => {
+                await client.req("POST", "/auth/reset", {
+                    token,
+                    ...data,
+                });
+                history.push("/login");
+            }}
+        />
+    );
 }
diff --git a/src/pages/login/forms/Legal.tsx b/src/pages/login/forms/Legal.tsx
index 754429b..1b1a1cb 100644
--- a/src/pages/login/forms/Legal.tsx
+++ b/src/pages/login/forms/Legal.tsx
@@ -2,19 +2,19 @@ import styles from "../Login.module.scss";
 import { Text } from "preact-i18n";
 
 export function Legal() {
-	return (
-		<span className={styles.footer}>
-			<a href="https://revolt.chat/about" target="_blank">
-				<Text id="general.about" />
-			</a>
-			&middot;
-			<a href="https://revolt.chat/terms" target="_blank">
-				<Text id="general.tos" />
-			</a>
-			&middot;
-			<a href="https://revolt.chat/privacy" target="_blank">
-				<Text id="general.privacy" />
-			</a>
-		</span>
-	);
+    return (
+        <span className={styles.footer}>
+            <a href="https://revolt.chat/about" target="_blank">
+                <Text id="general.about" />
+            </a>
+            &middot;
+            <a href="https://revolt.chat/terms" target="_blank">
+                <Text id="general.tos" />
+            </a>
+            &middot;
+            <a href="https://revolt.chat/privacy" target="_blank">
+                <Text id="general.privacy" />
+            </a>
+        </span>
+    );
 }
diff --git a/src/pages/login/forms/MailProvider.tsx b/src/pages/login/forms/MailProvider.tsx
index 8475f03..ffb71b4 100644
--- a/src/pages/login/forms/MailProvider.tsx
+++ b/src/pages/login/forms/MailProvider.tsx
@@ -4,53 +4,53 @@ import { Text } from "preact-i18n";
 import Button from "../../../components/ui/Button";
 
 interface Props {
-	email?: string;
+    email?: string;
 }
 
 function mapMailProvider(email?: string): [string, string] | undefined {
-	if (!email) return;
+    if (!email) return;
 
-	const match = /@(.+)/.exec(email);
-	if (match === null) return;
+    const match = /@(.+)/.exec(email);
+    if (match === null) return;
 
-	const domain = match[1];
-	switch (domain) {
-		case "gmail.com":
-			return ["Gmail", "https://gmail.com"];
-		case "tuta.io":
-			return ["Tutanota", "https://mail.tutanota.com"];
-		case "outlook.com":
-			return ["Outlook", "https://outlook.live.com"];
-		case "yahoo.com":
-			return ["Yahoo", "https://mail.yahoo.com"];
-		case "wp.pl":
-			return ["WP Poczta", "https://poczta.wp.pl"];
-		case "protonmail.com":
-		case "protonmail.ch":
-			return ["ProtonMail", "https://mail.protonmail.com"];
-		case "seznam.cz":
-		case "email.cz":
-		case "post.cz":
-			return ["Seznam", "https://email.seznam.cz"];
-		default:
-			return [domain, `https://${domain}`];
-	}
+    const domain = match[1];
+    switch (domain) {
+        case "gmail.com":
+            return ["Gmail", "https://gmail.com"];
+        case "tuta.io":
+            return ["Tutanota", "https://mail.tutanota.com"];
+        case "outlook.com":
+            return ["Outlook", "https://outlook.live.com"];
+        case "yahoo.com":
+            return ["Yahoo", "https://mail.yahoo.com"];
+        case "wp.pl":
+            return ["WP Poczta", "https://poczta.wp.pl"];
+        case "protonmail.com":
+        case "protonmail.ch":
+            return ["ProtonMail", "https://mail.protonmail.com"];
+        case "seznam.cz":
+        case "email.cz":
+        case "post.cz":
+            return ["Seznam", "https://email.seznam.cz"];
+        default:
+            return [domain, `https://${domain}`];
+    }
 }
 
 export function MailProvider({ email }: Props) {
-	const provider = mapMailProvider(email);
-	if (!provider) return null;
+    const provider = mapMailProvider(email);
+    if (!provider) return null;
 
-	return (
-		<div className={styles.mailProvider}>
-			<a href={provider[1]} target="_blank">
-				<Button>
-					<Text
-						id="login.open_mail_provider"
-						fields={{ provider: provider[0] }}
-					/>
-				</Button>
-			</a>
-		</div>
-	);
+    return (
+        <div className={styles.mailProvider}>
+            <a href={provider[1]} target="_blank">
+                <Button>
+                    <Text
+                        id="login.open_mail_provider"
+                        fields={{ provider: provider[0] }}
+                    />
+                </Button>
+            </a>
+        </div>
+    );
 }
diff --git a/src/pages/settings/ChannelSettings.tsx b/src/pages/settings/ChannelSettings.tsx
index 6720af5..4e12183 100644
--- a/src/pages/settings/ChannelSettings.tsx
+++ b/src/pages/settings/ChannelSettings.tsx
@@ -13,75 +13,75 @@ import Overview from "./channel/Overview";
 import Permissions from "./channel/Permissions";
 
 export default function ChannelSettings() {
-	const { channel: cid } = useParams<{ channel: string }>();
-	const ctx = useForceUpdate();
-	const channel = useChannel(cid, ctx);
-	if (!channel) return null;
-	if (
-		channel.channel_type === "SavedMessages" ||
-		channel.channel_type === "DirectMessage"
-	)
-		return null;
+    const { channel: cid } = useParams<{ channel: string }>();
+    const ctx = useForceUpdate();
+    const channel = useChannel(cid, ctx);
+    if (!channel) return null;
+    if (
+        channel.channel_type === "SavedMessages" ||
+        channel.channel_type === "DirectMessage"
+    )
+        return null;
 
-	const history = useHistory();
-	function switchPage(to?: string) {
-		let base_url;
-		switch (channel?.channel_type) {
-			case "TextChannel":
-			case "VoiceChannel":
-				base_url = `/server/${channel.server}/channel/${cid}/settings`;
-				break;
-			default:
-				base_url = `/channel/${cid}/settings`;
-		}
+    const history = useHistory();
+    function switchPage(to?: string) {
+        let base_url;
+        switch (channel?.channel_type) {
+            case "TextChannel":
+            case "VoiceChannel":
+                base_url = `/server/${channel.server}/channel/${cid}/settings`;
+                break;
+            default:
+                base_url = `/channel/${cid}/settings`;
+        }
 
-		if (to) {
-			history.replace(`${base_url}/${to}`);
-		} else {
-			history.replace(base_url);
-		}
-	}
+        if (to) {
+            history.replace(`${base_url}/${to}`);
+        } else {
+            history.replace(base_url);
+        }
+    }
 
-	return (
-		<GenericSettings
-			pages={[
-				{
-					category: (
-						<Category
-							variant="uniform"
-							text={getChannelName(ctx.client, channel, true)}
-						/>
-					),
-					id: "overview",
-					icon: <ListUl size={20} />,
-					title: (
-						<Text id="app.settings.channel_pages.overview.title" />
-					),
-				},
-				{
-					id: "permissions",
-					icon: <ListCheck size={20} />,
-					title: (
-						<Text id="app.settings.channel_pages.permissions.title" />
-					),
-				},
-			]}
-			children={[
-				<Route path="/server/:server/channel/:channel/settings/permissions">
-					<Permissions channel={channel} />
-				</Route>,
-				<Route path="/channel/:channel/settings/permissions">
-					<Permissions channel={channel} />
-				</Route>,
+    return (
+        <GenericSettings
+            pages={[
+                {
+                    category: (
+                        <Category
+                            variant="uniform"
+                            text={getChannelName(ctx.client, channel, true)}
+                        />
+                    ),
+                    id: "overview",
+                    icon: <ListUl size={20} />,
+                    title: (
+                        <Text id="app.settings.channel_pages.overview.title" />
+                    ),
+                },
+                {
+                    id: "permissions",
+                    icon: <ListCheck size={20} />,
+                    title: (
+                        <Text id="app.settings.channel_pages.permissions.title" />
+                    ),
+                },
+            ]}
+            children={[
+                <Route path="/server/:server/channel/:channel/settings/permissions">
+                    <Permissions channel={channel} />
+                </Route>,
+                <Route path="/channel/:channel/settings/permissions">
+                    <Permissions channel={channel} />
+                </Route>,
 
-				<Route path="/">
-					<Overview channel={channel} />
-				</Route>,
-			]}
-			category="channel_pages"
-			switchPage={switchPage}
-			defaultPage="overview"
-			showExitButton
-		/>
-	);
+                <Route path="/">
+                    <Overview channel={channel} />
+                </Route>,
+            ]}
+            category="channel_pages"
+            switchPage={switchPage}
+            defaultPage="overview"
+            showExitButton
+        />
+    );
 }
diff --git a/src/pages/settings/GenericSettings.tsx b/src/pages/settings/GenericSettings.tsx
index 2f64ecc..baea15a 100644
--- a/src/pages/settings/GenericSettings.tsx
+++ b/src/pages/settings/GenericSettings.tsx
@@ -19,140 +19,140 @@ import ButtonItem from "../../components/navigation/items/ButtonItem";
 import { Children } from "../../types/Preact";
 
 interface Props {
-	pages: {
-		category?: Children;
-		divider?: boolean;
-		id: string;
-		icon: Children;
-		title: Children;
-		hideTitle?: boolean;
-	}[];
-	custom?: Children;
-	children: Children;
-	defaultPage: string;
-	showExitButton?: boolean;
-	switchPage: (to?: string) => void;
-	category: "pages" | "channel_pages" | "server_pages";
+    pages: {
+        category?: Children;
+        divider?: boolean;
+        id: string;
+        icon: Children;
+        title: Children;
+        hideTitle?: boolean;
+    }[];
+    custom?: Children;
+    children: Children;
+    defaultPage: string;
+    showExitButton?: boolean;
+    switchPage: (to?: string) => void;
+    category: "pages" | "channel_pages" | "server_pages";
 }
 
 export function GenericSettings({
-	pages,
-	switchPage,
-	category,
-	custom,
-	children,
-	defaultPage,
-	showExitButton,
+    pages,
+    switchPage,
+    category,
+    custom,
+    children,
+    defaultPage,
+    showExitButton,
 }: Props) {
-	const history = useHistory();
-	const theme = useContext(ThemeContext);
-	const { page } = useParams<{ page: string }>();
+    const history = useHistory();
+    const theme = useContext(ThemeContext);
+    const { page } = useParams<{ page: string }>();
 
-	function exitSettings() {
-		if (history.length > 0) {
-			history.goBack();
-		} else {
-			history.push("/");
-		}
-	}
+    function exitSettings() {
+        if (history.length > 0) {
+            history.goBack();
+        } else {
+            history.push("/");
+        }
+    }
 
-	useEffect(() => {
-		function keyDown(e: KeyboardEvent) {
-			if (e.key === "Escape") {
-				exitSettings();
-			}
-		}
+    useEffect(() => {
+        function keyDown(e: KeyboardEvent) {
+            if (e.key === "Escape") {
+                exitSettings();
+            }
+        }
 
-		document.body.addEventListener("keydown", keyDown);
-		return () => document.body.removeEventListener("keydown", keyDown);
-	}, []);
+        document.body.addEventListener("keydown", keyDown);
+        return () => document.body.removeEventListener("keydown", keyDown);
+    }, []);
 
-	return (
-		<div className={styles.settings} data-mobile={isTouchscreenDevice}>
-			<Helmet>
-				<meta
-					name="theme-color"
-					content={
-						isTouchscreenDevice
-							? theme["primary-header"]
-							: theme["secondary-background"]
-					}
-				/>
-			</Helmet>
-			{isTouchscreenDevice && (
-				<Header placement="primary">
-					{typeof page === "undefined" ? (
-						<>
-							{showExitButton && (
-								<IconButton onClick={exitSettings}>
-									<X size={24} />
-								</IconButton>
-							)}
-							<Text id="app.settings.title" />
-						</>
-					) : (
-						<>
-							<IconButton onClick={() => switchPage()}>
-								<ArrowBack size={24} />
-							</IconButton>
-							<Text
-								id={`app.settings.${category}.${page}.title`}
-							/>
-						</>
-					)}
-				</Header>
-			)}
-			{(!isTouchscreenDevice || typeof page === "undefined") && (
-				<div className={styles.sidebar}>
-					<div className={styles.container}>
-						{pages.map((entry, i) => (
-							<>
-								{entry.category && (
-									<Category
-										variant="uniform"
-										text={entry.category}
-									/>
-								)}
-								<ButtonItem
-									active={
-										page === entry.id ||
-										(i === 0 &&
-											!isTouchscreenDevice &&
-											typeof page === "undefined")
-									}
-									onClick={() => switchPage(entry.id)}
-									compact>
-									{entry.icon} {entry.title}
-								</ButtonItem>
-								{entry.divider && <LineDivider />}
-							</>
-						))}
-						{custom}
-					</div>
-				</div>
-			)}
-			{(!isTouchscreenDevice || typeof page === "string") && (
-				<div className={styles.content}>
-					{!isTouchscreenDevice &&
-						!pages.find((x) => x.id === page && x.hideTitle) && (
-							<h1>
-								<Text
-									id={`app.settings.${category}.${
-										page ?? defaultPage
-									}.title`}
-								/>
-							</h1>
-						)}
-					<Switch>{children}</Switch>
-				</div>
-			)}
-			{!isTouchscreenDevice && (
-				<div className={styles.action}>
-					<IconButton onClick={exitSettings}>
-						<XCircle size={48} />
-					</IconButton>
-				</div>
-			)}
-		</div>
-	);
+    return (
+        <div className={styles.settings} data-mobile={isTouchscreenDevice}>
+            <Helmet>
+                <meta
+                    name="theme-color"
+                    content={
+                        isTouchscreenDevice
+                            ? theme["primary-header"]
+                            : theme["secondary-background"]
+                    }
+                />
+            </Helmet>
+            {isTouchscreenDevice && (
+                <Header placement="primary">
+                    {typeof page === "undefined" ? (
+                        <>
+                            {showExitButton && (
+                                <IconButton onClick={exitSettings}>
+                                    <X size={24} />
+                                </IconButton>
+                            )}
+                            <Text id="app.settings.title" />
+                        </>
+                    ) : (
+                        <>
+                            <IconButton onClick={() => switchPage()}>
+                                <ArrowBack size={24} />
+                            </IconButton>
+                            <Text
+                                id={`app.settings.${category}.${page}.title`}
+                            />
+                        </>
+                    )}
+                </Header>
+            )}
+            {(!isTouchscreenDevice || typeof page === "undefined") && (
+                <div className={styles.sidebar}>
+                    <div className={styles.container}>
+                        {pages.map((entry, i) => (
+                            <>
+                                {entry.category && (
+                                    <Category
+                                        variant="uniform"
+                                        text={entry.category}
+                                    />
+                                )}
+                                <ButtonItem
+                                    active={
+                                        page === entry.id ||
+                                        (i === 0 &&
+                                            !isTouchscreenDevice &&
+                                            typeof page === "undefined")
+                                    }
+                                    onClick={() => switchPage(entry.id)}
+                                    compact>
+                                    {entry.icon} {entry.title}
+                                </ButtonItem>
+                                {entry.divider && <LineDivider />}
+                            </>
+                        ))}
+                        {custom}
+                    </div>
+                </div>
+            )}
+            {(!isTouchscreenDevice || typeof page === "string") && (
+                <div className={styles.content}>
+                    {!isTouchscreenDevice &&
+                        !pages.find((x) => x.id === page && x.hideTitle) && (
+                            <h1>
+                                <Text
+                                    id={`app.settings.${category}.${
+                                        page ?? defaultPage
+                                    }.title`}
+                                />
+                            </h1>
+                        )}
+                    <Switch>{children}</Switch>
+                </div>
+            )}
+            {!isTouchscreenDevice && (
+                <div className={styles.action}>
+                    <IconButton onClick={exitSettings}>
+                        <XCircle size={48} />
+                    </IconButton>
+                </div>
+            )}
+        </div>
+    );
 }
diff --git a/src/pages/settings/ServerSettings.tsx b/src/pages/settings/ServerSettings.tsx
index bb0cde8..e3b2a2e 100644
--- a/src/pages/settings/ServerSettings.tsx
+++ b/src/pages/settings/ServerSettings.tsx
@@ -1,8 +1,8 @@
 import {
-	ListUl,
-	Share,
-	Group,
-	ListCheck,
+    ListUl,
+    Share,
+    Group,
+    ListCheck,
 } from "@styled-icons/boxicons-regular";
 import { XSquare } from "@styled-icons/boxicons-solid";
 import { Route, useHistory, useParams } from "react-router-dom";
@@ -22,85 +22,85 @@ import { Overview } from "./server/Overview";
 import { Roles } from "./server/Roles";
 
 export default function ServerSettings() {
-	const { server: sid } = useParams<{ server: string }>();
-	const server = useServer(sid);
-	if (!server) return null;
+    const { server: sid } = useParams<{ server: string }>();
+    const server = useServer(sid);
+    if (!server) return null;
 
-	const history = useHistory();
-	function switchPage(to?: string) {
-		if (to) {
-			history.replace(`/server/${sid}/settings/${to}`);
-		} else {
-			history.replace(`/server/${sid}/settings`);
-		}
-	}
+    const history = useHistory();
+    function switchPage(to?: string) {
+        if (to) {
+            history.replace(`/server/${sid}/settings/${to}`);
+        } else {
+            history.replace(`/server/${sid}/settings`);
+        }
+    }
 
-	return (
-		<GenericSettings
-			pages={[
-				{
-					category: <Category variant="uniform" text={server.name} />, //TOFIX: Just add the server.name as a string, otherwise it makes a duplicate category
-					id: "overview",
-					icon: <ListUl size={20} />,
-					title: (
-						<Text id="app.settings.server_pages.overview.title" />
-					),
-				},
-				{
-					id: "members",
-					icon: <Group size={20} />,
-					title: (
-						<Text id="app.settings.server_pages.members.title" />
-					),
-				},
-				{
-					id: "invites",
-					icon: <Share size={20} />,
-					title: (
-						<Text id="app.settings.server_pages.invites.title" />
-					),
-				},
-				{
-					id: "bans",
-					icon: <XSquare size={20} />,
-					title: <Text id="app.settings.server_pages.bans.title" />,
-				},
-				{
-					id: "roles",
-					icon: <ListCheck size={20} />,
-					title: <Text id="app.settings.server_pages.roles.title" />,
-					hideTitle: true,
-				},
-			]}
-			children={[
-				<Route path="/server/:server/settings/members">
-					<RequiresOnline>
-						<Members server={server} />
-					</RequiresOnline>
-				</Route>,
-				<Route path="/server/:server/settings/invites">
-					<RequiresOnline>
-						<Invites server={server} />
-					</RequiresOnline>
-				</Route>,
-				<Route path="/server/:server/settings/bans">
-					<RequiresOnline>
-						<Bans server={server} />
-					</RequiresOnline>
-				</Route>,
-				<Route path="/server/:server/settings/roles">
-					<RequiresOnline>
-						<Roles server={server} />
-					</RequiresOnline>
-				</Route>,
-				<Route path="/">
-					<Overview server={server} />
-				</Route>,
-			]}
-			category="server_pages"
-			switchPage={switchPage}
-			defaultPage="overview"
-			showExitButton
-		/>
-	);
+    return (
+        <GenericSettings
+            pages={[
+                {
+                    category: <Category variant="uniform" text={server.name} />, //TOFIX: Just add the server.name as a string, otherwise it makes a duplicate category
+                    id: "overview",
+                    icon: <ListUl size={20} />,
+                    title: (
+                        <Text id="app.settings.server_pages.overview.title" />
+                    ),
+                },
+                {
+                    id: "members",
+                    icon: <Group size={20} />,
+                    title: (
+                        <Text id="app.settings.server_pages.members.title" />
+                    ),
+                },
+                {
+                    id: "invites",
+                    icon: <Share size={20} />,
+                    title: (
+                        <Text id="app.settings.server_pages.invites.title" />
+                    ),
+                },
+                {
+                    id: "bans",
+                    icon: <XSquare size={20} />,
+                    title: <Text id="app.settings.server_pages.bans.title" />,
+                },
+                {
+                    id: "roles",
+                    icon: <ListCheck size={20} />,
+                    title: <Text id="app.settings.server_pages.roles.title" />,
+                    hideTitle: true,
+                },
+            ]}
+            children={[
+                <Route path="/server/:server/settings/members">
+                    <RequiresOnline>
+                        <Members server={server} />
+                    </RequiresOnline>
+                </Route>,
+                <Route path="/server/:server/settings/invites">
+                    <RequiresOnline>
+                        <Invites server={server} />
+                    </RequiresOnline>
+                </Route>,
+                <Route path="/server/:server/settings/bans">
+                    <RequiresOnline>
+                        <Bans server={server} />
+                    </RequiresOnline>
+                </Route>,
+                <Route path="/server/:server/settings/roles">
+                    <RequiresOnline>
+                        <Roles server={server} />
+                    </RequiresOnline>
+                </Route>,
+                <Route path="/">
+                    <Overview server={server} />
+                </Route>,
+            ]}
+            category="server_pages"
+            switchPage={switchPage}
+            defaultPage="overview"
+            showExitButton
+        />
+    );
 }
diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx
index fba14e3..bb23da4 100644
--- a/src/pages/settings/Settings.tsx
+++ b/src/pages/settings/Settings.tsx
@@ -1,18 +1,18 @@
 import { Gitlab } from "@styled-icons/boxicons-logos";
 import {
-	Sync as SyncIcon,
-	Globe,
-	LogOut,
+    Sync as SyncIcon,
+    Globe,
+    LogOut,
 } from "@styled-icons/boxicons-regular";
 import {
-	Bell,
-	Palette,
-	Coffee,
-	IdCard,
-	CheckShield,
-	Flask,
-	User,
-	Megaphone,
+    Bell,
+    Palette,
+    Coffee,
+    IdCard,
+    CheckShield,
+    Flask,
+    User,
+    Megaphone,
 } from "@styled-icons/boxicons-solid";
 import { Route, useHistory } from "react-router-dom";
 import { LIBRARY_VERSION } from "revolt.js";
@@ -23,8 +23,8 @@ import { useContext } from "preact/hooks";
 
 import RequiresOnline from "../../context/revoltjs/RequiresOnline";
 import {
-	AppContext,
-	OperationsContext,
+    AppContext,
+    OperationsContext,
 } from "../../context/revoltjs/RevoltClient";
 
 import LineDivider from "../../components/ui/LineDivider";
@@ -44,159 +44,159 @@ import { Sessions } from "./panes/Sessions";
 import { Sync } from "./panes/Sync";
 
 export default function Settings() {
-	const history = useHistory();
-	const client = useContext(AppContext);
-	const operations = useContext(OperationsContext);
+    const history = useHistory();
+    const client = useContext(AppContext);
+    const operations = useContext(OperationsContext);
 
-	function switchPage(to?: string) {
-		if (to) {
-			history.replace(`/settings/${to}`);
-		} else {
-			history.replace(`/settings`);
-		}
-	}
+    function switchPage(to?: string) {
+        if (to) {
+            history.replace(`/settings/${to}`);
+        } else {
+            history.replace(`/settings`);
+        }
+    }
 
-	return (
-		<GenericSettings
-			pages={[
-				{
-					category: (
-						<Text id="app.settings.categories.user_settings" />
-					),
-					id: "account",
-					icon: <User size={20} />,
-					title: <Text id="app.settings.pages.account.title" />,
-				},
-				{
-					id: "profile",
-					icon: <IdCard size={20} />,
-					title: <Text id="app.settings.pages.profile.title" />,
-				},
-				{
-					id: "sessions",
-					icon: <CheckShield size={20} />,
-					title: <Text id="app.settings.pages.sessions.title" />,
-				},
-				{
-					category: (
-						<Text id="app.settings.categories.client_settings" />
-					),
-					id: "appearance",
-					icon: <Palette size={20} />,
-					title: <Text id="app.settings.pages.appearance.title" />,
-				},
-				{
-					id: "notifications",
-					icon: <Bell size={20} />,
-					title: <Text id="app.settings.pages.notifications.title" />,
-				},
-				{
-					id: "language",
-					icon: <Globe size={20} />,
-					title: <Text id="app.settings.pages.language.title" />,
-				},
-				{
-					id: "sync",
-					icon: <SyncIcon size={20} />,
-					title: <Text id="app.settings.pages.sync.title" />,
-				},
-				{
-					divider: true,
-					id: "experiments",
-					icon: <Flask size={20} />,
-					title: <Text id="app.settings.pages.experiments.title" />,
-				},
-				{
-					id: "feedback",
-					icon: <Megaphone size={20} />,
-					title: <Text id="app.settings.pages.feedback.title" />,
-				},
-			]}
-			children={[
-				<Route path="/settings/profile">
-					<Profile />
-				</Route>,
-				<Route path="/settings/sessions">
-					<RequiresOnline>
-						<Sessions />
-					</RequiresOnline>
-				</Route>,
-				<Route path="/settings/appearance">
-					<Appearance />
-				</Route>,
-				<Route path="/settings/notifications">
-					<Notifications />
-				</Route>,
-				<Route path="/settings/language">
-					<Languages />
-				</Route>,
-				<Route path="/settings/sync">
-					<Sync />
-				</Route>,
-				<Route path="/settings/experiments">
-					<ExperimentsPage />
-				</Route>,
-				<Route path="/settings/feedback">
-					<Feedback />
-				</Route>,
-				<Route path="/">
-					<Account />
-				</Route>,
-			]}
-			defaultPage="account"
-			switchPage={switchPage}
-			category="pages"
-			custom={[
-				<a href="https://gitlab.insrt.uk/revolt" target="_blank">
-					<ButtonItem compact>
-						<Gitlab size={20} />
-						<Text id="app.settings.pages.source_code" />
-					</ButtonItem>
-				</a>,
-				<a href="https://ko-fi.com/insertish" target="_blank">
-					<ButtonItem className={styles.donate} compact>
-						<Coffee size={20} />
-						<Text id="app.settings.pages.donate.title" />
-					</ButtonItem>
-				</a>,
-				<LineDivider />,
-				<ButtonItem
-					onClick={() => operations.logout()}
-					className={styles.logOut}
-					compact>
-					<LogOut size={20} />
-					<Text id="app.settings.pages.logOut" />
-				</ButtonItem>,
-				<div className={styles.version}>
-					<div>
-						<span className={styles.revision}>
-							<a
-								href={`${REPO_URL}/${GIT_REVISION}`}
-								target="_blank">
-								{GIT_REVISION.substr(0, 7)}
-							</a>
-							{` `}
-							<a
-								href={
-									GIT_BRANCH !== "DETACHED"
-										? `https://gitlab.insrt.uk/revolt/client/-/tree/${GIT_BRANCH}`
-										: undefined
-								}
-								target="_blank">
-								({GIT_BRANCH})
-							</a>
-						</span>
-						<span>
-							{GIT_BRANCH === "production" ? "Stable" : "Nightly"}{" "}
-							{APP_VERSION}
-						</span>
-						<span>
-							API: {client.configuration?.revolt ?? "N/A"}
-						</span>
-						<span>revolt.js: {LIBRARY_VERSION}</span>
-					</div>
-				</div>,
-			]}
-		/>
-	);
+    return (
+        <GenericSettings
+            pages={[
+                {
+                    category: (
+                        <Text id="app.settings.categories.user_settings" />
+                    ),
+                    id: "account",
+                    icon: <User size={20} />,
+                    title: <Text id="app.settings.pages.account.title" />,
+                },
+                {
+                    id: "profile",
+                    icon: <IdCard size={20} />,
+                    title: <Text id="app.settings.pages.profile.title" />,
+                },
+                {
+                    id: "sessions",
+                    icon: <CheckShield size={20} />,
+                    title: <Text id="app.settings.pages.sessions.title" />,
+                },
+                {
+                    category: (
+                        <Text id="app.settings.categories.client_settings" />
+                    ),
+                    id: "appearance",
+                    icon: <Palette size={20} />,
+                    title: <Text id="app.settings.pages.appearance.title" />,
+                },
+                {
+                    id: "notifications",
+                    icon: <Bell size={20} />,
+                    title: <Text id="app.settings.pages.notifications.title" />,
+                },
+                {
+                    id: "language",
+                    icon: <Globe size={20} />,
+                    title: <Text id="app.settings.pages.language.title" />,
+                },
+                {
+                    id: "sync",
+                    icon: <SyncIcon size={20} />,
+                    title: <Text id="app.settings.pages.sync.title" />,
+                },
+                {
+                    divider: true,
+                    id: "experiments",
+                    icon: <Flask size={20} />,
+                    title: <Text id="app.settings.pages.experiments.title" />,
+                },
+                {
+                    id: "feedback",
+                    icon: <Megaphone size={20} />,
+                    title: <Text id="app.settings.pages.feedback.title" />,
+                },
+            ]}
+            children={[
+                <Route path="/settings/profile">
+                    <Profile />
+                </Route>,
+                <Route path="/settings/sessions">
+                    <RequiresOnline>
+                        <Sessions />
+                    </RequiresOnline>
+                </Route>,
+                <Route path="/settings/appearance">
+                    <Appearance />
+                </Route>,
+                <Route path="/settings/notifications">
+                    <Notifications />
+                </Route>,
+                <Route path="/settings/language">
+                    <Languages />
+                </Route>,
+                <Route path="/settings/sync">
+                    <Sync />
+                </Route>,
+                <Route path="/settings/experiments">
+                    <ExperimentsPage />
+                </Route>,
+                <Route path="/settings/feedback">
+                    <Feedback />
+                </Route>,
+                <Route path="/">
+                    <Account />
+                </Route>,
+            ]}
+            defaultPage="account"
+            switchPage={switchPage}
+            category="pages"
+            custom={[
+                <a href="https://gitlab.insrt.uk/revolt" target="_blank">
+                    <ButtonItem compact>
+                        <Gitlab size={20} />
+                        <Text id="app.settings.pages.source_code" />
+                    </ButtonItem>
+                </a>,
+                <a href="https://ko-fi.com/insertish" target="_blank">
+                    <ButtonItem className={styles.donate} compact>
+                        <Coffee size={20} />
+                        <Text id="app.settings.pages.donate.title" />
+                    </ButtonItem>
+                </a>,
+                <LineDivider />,
+                <ButtonItem
+                    onClick={() => operations.logout()}
+                    className={styles.logOut}
+                    compact>
+                    <LogOut size={20} />
+                    <Text id="app.settings.pages.logOut" />
+                </ButtonItem>,
+                <div className={styles.version}>
+                    <div>
+                        <span className={styles.revision}>
+                            <a
+                                href={`${REPO_URL}/${GIT_REVISION}`}
+                                target="_blank">
+                                {GIT_REVISION.substr(0, 7)}
+                            </a>
+                            {` `}
+                            <a
+                                href={
+                                    GIT_BRANCH !== "DETACHED"
+                                        ? `https://gitlab.insrt.uk/revolt/client/-/tree/${GIT_BRANCH}`
+                                        : undefined
+                                }
+                                target="_blank">
+                                ({GIT_BRANCH})
+                            </a>
+                        </span>
+                        <span>
+                            {GIT_BRANCH === "production" ? "Stable" : "Nightly"}{" "}
+                            {APP_VERSION}
+                        </span>
+                        <span>
+                            API: {client.configuration?.revolt ?? "N/A"}
+                        </span>
+                        <span>revolt.js: {LIBRARY_VERSION}</span>
+                    </div>
+                </div>,
+            ]}
+        />
+    );
 }
diff --git a/src/pages/settings/channel/Overview.tsx b/src/pages/settings/channel/Overview.tsx
index 6df4e5a..b8fbcdf 100644
--- a/src/pages/settings/channel/Overview.tsx
+++ b/src/pages/settings/channel/Overview.tsx
@@ -13,105 +13,105 @@ import Button from "../../../components/ui/Button";
 import InputBox from "../../../components/ui/InputBox";
 
 interface Props {
-	channel:
-		| Channels.GroupChannel
-		| Channels.TextChannel
-		| Channels.VoiceChannel;
+    channel:
+        | Channels.GroupChannel
+        | Channels.TextChannel
+        | Channels.VoiceChannel;
 }
 
 export default function Overview({ channel }: Props) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	const [name, setName] = useState(channel.name);
-	const [description, setDescription] = useState(channel.description ?? "");
+    const [name, setName] = useState(channel.name);
+    const [description, setDescription] = useState(channel.description ?? "");
 
-	useEffect(() => setName(channel.name), [channel.name]);
-	useEffect(
-		() => setDescription(channel.description ?? ""),
-		[channel.description],
-	);
+    useEffect(() => setName(channel.name), [channel.name]);
+    useEffect(
+        () => setDescription(channel.description ?? ""),
+        [channel.description],
+    );
 
-	const [changed, setChanged] = useState(false);
-	function save() {
-		let changes: any = {};
-		if (name !== channel.name) changes.name = name;
-		if (description !== channel.description)
-			changes.description = description;
+    const [changed, setChanged] = useState(false);
+    function save() {
+        let changes: any = {};
+        if (name !== channel.name) changes.name = name;
+        if (description !== channel.description)
+            changes.description = description;
 
-		client.channels.edit(channel._id, changes);
-		setChanged(false);
-	}
+        client.channels.edit(channel._id, changes);
+        setChanged(false);
+    }
 
-	return (
-		<div className={styles.overview}>
-			<div className={styles.row}>
-				<FileUploader
-					width={80}
-					height={80}
-					style="icon"
-					fileType="icons"
-					behaviour="upload"
-					maxFileSize={2_500_000}
-					onUpload={(icon) =>
-						client.channels.edit(channel._id, { icon })
-					}
-					previewURL={client.channels.getIconURL(
-						channel._id,
-						{ max_side: 256 },
-						true,
-					)}
-					remove={() =>
-						client.channels.edit(channel._id, { remove: "Icon" })
-					}
-					defaultPreview={
-						channel.channel_type === "Group"
-							? "/assets/group.png"
-							: undefined
-					}
-				/>
-				<div className={styles.name}>
-					<h3>
-						{channel.channel_type === "Group" ? (
-							<Text id="app.main.groups.name" />
-						) : (
-							<Text id="app.main.servers.channel_name" />
-						)}
-					</h3>
-					<InputBox
-						contrast
-						value={name}
-						maxLength={32}
-						onChange={(e) => {
-							setName(e.currentTarget.value);
-							if (!changed) setChanged(true);
-						}}
-					/>
-				</div>
-			</div>
+    return (
+        <div className={styles.overview}>
+            <div className={styles.row}>
+                <FileUploader
+                    width={80}
+                    height={80}
+                    style="icon"
+                    fileType="icons"
+                    behaviour="upload"
+                    maxFileSize={2_500_000}
+                    onUpload={(icon) =>
+                        client.channels.edit(channel._id, { icon })
+                    }
+                    previewURL={client.channels.getIconURL(
+                        channel._id,
+                        { max_side: 256 },
+                        true,
+                    )}
+                    remove={() =>
+                        client.channels.edit(channel._id, { remove: "Icon" })
+                    }
+                    defaultPreview={
+                        channel.channel_type === "Group"
+                            ? "/assets/group.png"
+                            : undefined
+                    }
+                />
+                <div className={styles.name}>
+                    <h3>
+                        {channel.channel_type === "Group" ? (
+                            <Text id="app.main.groups.name" />
+                        ) : (
+                            <Text id="app.main.servers.channel_name" />
+                        )}
+                    </h3>
+                    <InputBox
+                        contrast
+                        value={name}
+                        maxLength={32}
+                        onChange={(e) => {
+                            setName(e.currentTarget.value);
+                            if (!changed) setChanged(true);
+                        }}
+                    />
+                </div>
+            </div>
 
-			<h3>
-				{channel.channel_type === "Group" ? (
-					<Text id="app.main.groups.description" />
-				) : (
-					<Text id="app.main.servers.channel_description" />
-				)}
-			</h3>
-			<TextAreaAutoSize
-				maxRows={10}
-				minHeight={60}
-				maxLength={1024}
-				value={description}
-				placeholder={"Add a description..."}
-				onChange={(ev) => {
-					setDescription(ev.currentTarget.value);
-					if (!changed) setChanged(true);
-				}}
-			/>
-			<p>
-				<Button onClick={save} contrast disabled={!changed}>
-					<Text id="app.special.modals.actions.save" />
-				</Button>
-			</p>
-		</div>
-	);
+            <h3>
+                {channel.channel_type === "Group" ? (
+                    <Text id="app.main.groups.description" />
+                ) : (
+                    <Text id="app.main.servers.channel_description" />
+                )}
+            </h3>
+            <TextAreaAutoSize
+                maxRows={10}
+                minHeight={60}
+                maxLength={1024}
+                value={description}
+                placeholder={"Add a description..."}
+                onChange={(ev) => {
+                    setDescription(ev.currentTarget.value);
+                    if (!changed) setChanged(true);
+                }}
+            />
+            <p>
+                <Button onClick={save} contrast disabled={!changed}>
+                    <Text id="app.special.modals.actions.save" />
+                </Button>
+            </p>
+        </div>
+    );
 }
diff --git a/src/pages/settings/channel/Permissions.tsx b/src/pages/settings/channel/Permissions.tsx
index c546721..b4d3083 100644
--- a/src/pages/settings/channel/Permissions.tsx
+++ b/src/pages/settings/channel/Permissions.tsx
@@ -12,100 +12,100 @@ import Tip from "../../../components/ui/Tip";
 
 // ! FIXME: export from revolt.js
 const DEFAULT_PERMISSION_DM =
-	ChannelPermission.View +
-	ChannelPermission.SendMessage +
-	ChannelPermission.ManageChannel +
-	ChannelPermission.VoiceCall +
-	ChannelPermission.InviteOthers +
-	ChannelPermission.EmbedLinks +
-	ChannelPermission.UploadFiles;
+    ChannelPermission.View +
+    ChannelPermission.SendMessage +
+    ChannelPermission.ManageChannel +
+    ChannelPermission.VoiceCall +
+    ChannelPermission.InviteOthers +
+    ChannelPermission.EmbedLinks +
+    ChannelPermission.UploadFiles;
 
 interface Props {
-	channel:
-		| Channels.GroupChannel
-		| Channels.TextChannel
-		| Channels.VoiceChannel;
+    channel:
+        | Channels.GroupChannel
+        | Channels.TextChannel
+        | Channels.VoiceChannel;
 }
 
 // ! FIXME: bad code :)
 export default function Permissions({ channel }: Props) {
-	const [selected, setSelected] = useState("default");
-	const client = useContext(AppContext);
+    const [selected, setSelected] = useState("default");
+    const client = useContext(AppContext);
 
-	type R = { name: string; permissions: number };
-	let roles: { [key: string]: R } = {};
-	if (channel.channel_type !== "Group") {
-		const server = useServer(channel.server);
-		const a = server?.roles ?? {};
-		for (let b of Object.keys(a)) {
-			roles[b] = {
-				name: a[b].name,
-				permissions: a[b].permissions[1],
-			};
-		}
-	}
+    type R = { name: string; permissions: number };
+    let roles: { [key: string]: R } = {};
+    if (channel.channel_type !== "Group") {
+        const server = useServer(channel.server);
+        const a = server?.roles ?? {};
+        for (let b of Object.keys(a)) {
+            roles[b] = {
+                name: a[b].name,
+                permissions: a[b].permissions[1],
+            };
+        }
+    }
 
-	const keys = ["default", ...Object.keys(roles)];
+    const keys = ["default", ...Object.keys(roles)];
 
-	const defaultRole = {
-		name: "Default",
-		permissions:
-			(channel.channel_type === "Group"
-				? channel.permissions
-				: channel.default_permissions) ?? DEFAULT_PERMISSION_DM,
-	};
-	const selectedRole = selected === "default" ? defaultRole : roles[selected];
+    const defaultRole = {
+        name: "Default",
+        permissions:
+            (channel.channel_type === "Group"
+                ? channel.permissions
+                : channel.default_permissions) ?? DEFAULT_PERMISSION_DM,
+    };
+    const selectedRole = selected === "default" ? defaultRole : roles[selected];
 
-	if (!selectedRole) {
-		useEffect(() => setSelected("default"), []);
-		return null;
-	}
+    if (!selectedRole) {
+        useEffect(() => setSelected("default"), []);
+        return null;
+    }
 
-	const [p, setPerm] = useState(selectedRole.permissions >>> 0);
+    const [p, setPerm] = useState(selectedRole.permissions >>> 0);
 
-	useEffect(() => {
-		setPerm(selectedRole.permissions >>> 0);
-	}, [selected, selectedRole.permissions]);
+    useEffect(() => {
+        setPerm(selectedRole.permissions >>> 0);
+    }, [selected, selectedRole.permissions]);
 
-	return (
-		<div>
-			<Tip warning>This section is under construction.</Tip>
-			<h2>select role</h2>
-			{selected}
-			{keys.map((id) => {
-				let role: R = id === "default" ? defaultRole : roles[id];
+    return (
+        <div>
+            <Tip warning>This section is under construction.</Tip>
+            <h2>select role</h2>
+            {selected}
+            {keys.map((id) => {
+                let role: R = id === "default" ? defaultRole : roles[id];
 
-				return (
-					<Checkbox
-						checked={selected === id}
-						onChange={(selected) => selected && setSelected(id)}>
-						{role.name}
-					</Checkbox>
-				);
-			})}
-			<h2>channel per??issions</h2>
-			{Object.keys(ChannelPermission).map((perm) => {
-				let value =
-					ChannelPermission[perm as keyof typeof ChannelPermission];
-				if (value & DEFAULT_PERMISSION_DM) {
-					return (
-						<Checkbox
-							checked={(p & value) > 0}
-							onChange={(c) =>
-								setPerm(c ? p | value : p ^ value)
-							}>
-							{perm}
-						</Checkbox>
-					);
-				}
-			})}
-			<Button
-				contrast
-				onClick={() => {
-					client.channels.setPermissions(channel._id, selected, p);
-				}}>
-				click here to save permissions for role
-			</Button>
-		</div>
-	);
+                return (
+                    <Checkbox
+                        checked={selected === id}
+                        onChange={(selected) => selected && setSelected(id)}>
+                        {role.name}
+                    </Checkbox>
+                );
+            })}
+            <h2>channel per??issions</h2>
+            {Object.keys(ChannelPermission).map((perm) => {
+                let value =
+                    ChannelPermission[perm as keyof typeof ChannelPermission];
+                if (value & DEFAULT_PERMISSION_DM) {
+                    return (
+                        <Checkbox
+                            checked={(p & value) > 0}
+                            onChange={(c) =>
+                                setPerm(c ? p | value : p ^ value)
+                            }>
+                            {perm}
+                        </Checkbox>
+                    );
+                }
+            })}
+            <Button
+                contrast
+                onClick={() => {
+                    client.channels.setPermissions(channel._id, selected, p);
+                }}>
+                click here to save permissions for role
+            </Button>
+        </div>
+    );
 }
diff --git a/src/pages/settings/panes/Account.tsx b/src/pages/settings/panes/Account.tsx
index 08beb2e..3ebd443 100644
--- a/src/pages/settings/panes/Account.tsx
+++ b/src/pages/settings/panes/Account.tsx
@@ -9,8 +9,8 @@ import { useContext, useEffect, useState } from "preact/hooks";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 import {
-	ClientStatus,
-	StatusContext,
+    ClientStatus,
+    StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
 import { useForceUpdate, useSelf } from "../../../context/revoltjs/hooks";
 
@@ -20,87 +20,87 @@ import Overline from "../../../components/ui/Overline";
 import Tip from "../../../components/ui/Tip";
 
 export function Account() {
-	const { openScreen } = useIntermediate();
-	const status = useContext(StatusContext);
+    const { openScreen } = useIntermediate();
+    const status = useContext(StatusContext);
 
-	const ctx = useForceUpdate();
-	const user = useSelf(ctx);
-	if (!user) return null;
+    const ctx = useForceUpdate();
+    const user = useSelf(ctx);
+    if (!user) return null;
 
-	const [email, setEmail] = useState("...");
-	const [profile, setProfile] = useState<undefined | Users.Profile>(
-		undefined,
-	);
-	const history = useHistory();
+    const [email, setEmail] = useState("...");
+    const [profile, setProfile] = useState<undefined | Users.Profile>(
+        undefined,
+    );
+    const history = useHistory();
 
-	function switchPage(to: string) {
-		history.replace(`/settings/${to}`);
-	}
+    function switchPage(to: string) {
+        history.replace(`/settings/${to}`);
+    }
 
-	useEffect(() => {
-		if (email === "..." && status === ClientStatus.ONLINE) {
-			ctx.client
-				.req("GET", "/auth/user")
-				.then((account) => setEmail(account.email));
-		}
+    useEffect(() => {
+        if (email === "..." && status === ClientStatus.ONLINE) {
+            ctx.client
+                .req("GET", "/auth/user")
+                .then((account) => setEmail(account.email));
+        }
 
-		if (profile === undefined && status === ClientStatus.ONLINE) {
-			ctx.client.users
-				.fetchProfile(user._id)
-				.then((profile) => setProfile(profile ?? {}));
-		}
-	}, [status]);
+        if (profile === undefined && status === ClientStatus.ONLINE) {
+            ctx.client.users
+                .fetchProfile(user._id)
+                .then((profile) => setProfile(profile ?? {}));
+        }
+    }, [status]);
 
-	return (
-		<div className={styles.user}>
-			<div className={styles.banner}>
-				<UserIcon
-					className={styles.avatar}
-					target={user}
-					size={72}
-					onClick={() => switchPage("profile")}
-				/>
-				<div className={styles.username}>@{user.username}</div>
-			</div>
-			<div className={styles.details}>
-				{(
-					[
-						["username", user.username, <At size={24} />],
-						["email", email, <Envelope size={24} />],
-						["password", "*****", <Key size={24} />],
-					] as const
-				).map(([field, value, icon]) => (
-					<div>
-						{icon}
-						<div className={styles.detail}>
-							<Overline>
-								<Text id={`login.${field}`} />
-							</Overline>
-							<p>{value}</p>
-						</div>
-						<div>
-							<Button
-								onClick={() =>
-									openScreen({
-										id: "modify_account",
-										field: field,
-									})
-								}
-								contrast>
-								<Text id="app.settings.pages.account.change_field" />
-							</Button>
-						</div>
-					</div>
-				))}
-			</div>
-			<Tip>
-				<span>
-					<Text id="app.settings.tips.account.a" />
-				</span>{" "}
-				<a onClick={() => switchPage("profile")}>
-					<Text id="app.settings.tips.account.b" />
-				</a>
-			</Tip>
-		</div>
-	);
+    return (
+        <div className={styles.user}>
+            <div className={styles.banner}>
+                <UserIcon
+                    className={styles.avatar}
+                    target={user}
+                    size={72}
+                    onClick={() => switchPage("profile")}
+                />
+                <div className={styles.username}>@{user.username}</div>
+            </div>
+            <div className={styles.details}>
+                {(
+                    [
+                        ["username", user.username, <At size={24} />],
+                        ["email", email, <Envelope size={24} />],
+                        ["password", "*****", <Key size={24} />],
+                    ] as const
+                ).map(([field, value, icon]) => (
+                    <div>
+                        {icon}
+                        <div className={styles.detail}>
+                            <Overline>
+                                <Text id={`login.${field}`} />
+                            </Overline>
+                            <p>{value}</p>
+                        </div>
+                        <div>
+                            <Button
+                                onClick={() =>
+                                    openScreen({
+                                        id: "modify_account",
+                                        field: field,
+                                    })
+                                }
+                                contrast>
+                                <Text id="app.settings.pages.account.change_field" />
+                            </Button>
+                        </div>
+                    </div>
+                ))}
+            </div>
+            <Tip>
+                <span>
+                    <Text id="app.settings.tips.account.a" />
+                </span>{" "}
+                <a onClick={() => switchPage("profile")}>
+                    <Text id="app.settings.tips.account.b" />
+                </a>
+            </Tip>
+        </div>
+    );
 }
diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx
index 337e745..7e16d1c 100644
--- a/src/pages/settings/panes/Appearance.tsx
+++ b/src/pages/settings/panes/Appearance.tsx
@@ -13,15 +13,15 @@ import { connectState } from "../../../redux/connector";
 import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
 
 import {
-	DEFAULT_FONT,
-	DEFAULT_MONO_FONT,
-	FONTS,
-	FONT_KEYS,
-	MONOSCAPE_FONTS,
-	MONOSCAPE_FONT_KEYS,
-	Theme,
-	ThemeContext,
-	ThemeOptions,
+    DEFAULT_FONT,
+    DEFAULT_MONO_FONT,
+    FONTS,
+    FONT_KEYS,
+    MONOSCAPE_FONTS,
+    MONOSCAPE_FONT_KEYS,
+    Theme,
+    ThemeContext,
+    ThemeOptions,
 } from "../../../context/Theme";
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
 
@@ -39,92 +39,92 @@ import openmojiSVG from "../assets/openmoji_emoji.svg";
 import twemojiSVG from "../assets/twemoji_emoji.svg";
 
 interface Props {
-	settings: Settings;
+    settings: Settings;
 }
 
 // ! FIXME: code needs to be rewritten to fix jittering
 export function Component(props: Props) {
-	const theme = useContext(ThemeContext);
-	const { writeClipboard, openScreen } = useIntermediate();
+    const theme = useContext(ThemeContext);
+    const { writeClipboard, openScreen } = useIntermediate();
 
-	function setTheme(theme: ThemeOptions) {
-		dispatch({
-			type: "SETTINGS_SET_THEME",
-			theme,
-		});
-	}
+    function setTheme(theme: ThemeOptions) {
+        dispatch({
+            type: "SETTINGS_SET_THEME",
+            theme,
+        });
+    }
 
-	function pushOverride(custom: Partial<Theme>) {
-		dispatch({
-			type: "SETTINGS_SET_THEME_OVERRIDE",
-			custom,
-		});
-	}
+    function pushOverride(custom: Partial<Theme>) {
+        dispatch({
+            type: "SETTINGS_SET_THEME_OVERRIDE",
+            custom,
+        });
+    }
 
-	function setAccent(accent: string) {
-		setOverride({
-			accent,
-			"scrollbar-thumb": pSBC(-0.2, accent),
-		});
-	}
+    function setAccent(accent: string) {
+        setOverride({
+            accent,
+            "scrollbar-thumb": pSBC(-0.2, accent),
+        });
+    }
 
-	const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
-	function setEmojiPack(emojiPack: EmojiPacks) {
-		dispatch({
-			type: "SETTINGS_SET_APPEARANCE",
-			options: {
-				emojiPack,
-			},
-		});
-	}
+    const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
+    function setEmojiPack(emojiPack: EmojiPacks) {
+        dispatch({
+            type: "SETTINGS_SET_APPEARANCE",
+            options: {
+                emojiPack,
+            },
+        });
+    }
 
-	const setOverride = useCallback(debounce(pushOverride, 200), []) as (
-		custom: Partial<Theme>,
-	) => void;
-	const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
+    const setOverride = useCallback(debounce(pushOverride, 200), []) as (
+        custom: Partial<Theme>,
+    ) => void;
+    const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
 
-	useEffect(() => setOverride({ css }), [css]);
+    useEffect(() => setOverride({ css }), [css]);
 
-	const selected = props.settings.theme?.preset ?? "dark";
-	return (
-		<div className={styles.appearance}>
-			<h3>
-				<Text id="app.settings.pages.appearance.theme" />
-			</h3>
-			<div className={styles.themes}>
-				<div className={styles.theme}>
-					<img
-						src={lightSVG}
-						data-active={selected === "light"}
-						onClick={() =>
-							selected !== "light" &&
-							setTheme({ preset: "light" })
-						}
-					/>
-					<h4>
-						<Text id="app.settings.pages.appearance.color.light" />
-					</h4>
-				</div>
-				<div className={styles.theme}>
-					<img
-						src={darkSVG}
-						data-active={selected === "dark"}
-						onClick={() =>
-							selected !== "dark" && setTheme({ preset: "dark" })
-						}
-					/>
-					<h4>
-						<Text id="app.settings.pages.appearance.color.dark" />
-					</h4>
-				</div>
-			</div>
+    const selected = props.settings.theme?.preset ?? "dark";
+    return (
+        <div className={styles.appearance}>
+            <h3>
+                <Text id="app.settings.pages.appearance.theme" />
+            </h3>
+            <div className={styles.themes}>
+                <div className={styles.theme}>
+                    <img
+                        src={lightSVG}
+                        data-active={selected === "light"}
+                        onClick={() =>
+                            selected !== "light" &&
+                            setTheme({ preset: "light" })
+                        }
+                    />
+                    <h4>
+                        <Text id="app.settings.pages.appearance.color.light" />
+                    </h4>
+                </div>
+                <div className={styles.theme}>
+                    <img
+                        src={darkSVG}
+                        data-active={selected === "dark"}
+                        onClick={() =>
+                            selected !== "dark" && setTheme({ preset: "dark" })
+                        }
+                    />
+                    <h4>
+                        <Text id="app.settings.pages.appearance.color.dark" />
+                    </h4>
+                </div>
+            </div>
 
-			<h3>
-				<Text id="app.settings.pages.appearance.accent_selector" />
-			</h3>
-			<ColourSwatches value={theme.accent} onChange={setAccent} />
+            <h3>
+                <Text id="app.settings.pages.appearance.accent_selector" />
+            </h3>
+            <ColourSwatches value={theme.accent} onChange={setAccent} />
 
-			{/*<h3>
+            {/*<h3>
                 <Text id="app.settings.pages.appearance.message_display" />
             </h3>
             <div className={styles.display}>
@@ -146,229 +146,229 @@ export function Component(props: Props) {
                 </Radio>
             </div>*/}
 
-			<h3>
-				<Text id="app.settings.pages.appearance.font" />
-			</h3>
-			<ComboBox
-				value={theme.font ?? DEFAULT_FONT}
-				onChange={(e) =>
-					setTheme({ custom: { font: e.currentTarget.value as any } })
-				}>
-				{FONT_KEYS.map((key) => (
-					<option value={key}>
-						{FONTS[key as keyof typeof FONTS].name}
-					</option>
-				))}
-			</ComboBox>
-			<p>
-				<Checkbox
-					checked={props.settings.theme?.ligatures === true}
-					onChange={() =>
-						setTheme({
-							ligatures: !props.settings.theme?.ligatures,
-						})
-					}
-					description={
-						<Text id="app.settings.pages.appearance.ligatures_desc" />
-					}>
-					<Text id="app.settings.pages.appearance.ligatures" />
-				</Checkbox>
-			</p>
+            <h3>
+                <Text id="app.settings.pages.appearance.font" />
+            </h3>
+            <ComboBox
+                value={theme.font ?? DEFAULT_FONT}
+                onChange={(e) =>
+                    setTheme({ custom: { font: e.currentTarget.value as any } })
+                }>
+                {FONT_KEYS.map((key) => (
+                    <option value={key}>
+                        {FONTS[key as keyof typeof FONTS].name}
+                    </option>
+                ))}
+            </ComboBox>
+            <p>
+                <Checkbox
+                    checked={props.settings.theme?.ligatures === true}
+                    onChange={() =>
+                        setTheme({
+                            ligatures: !props.settings.theme?.ligatures,
+                        })
+                    }
+                    description={
+                        <Text id="app.settings.pages.appearance.ligatures_desc" />
+                    }>
+                    <Text id="app.settings.pages.appearance.ligatures" />
+                </Checkbox>
+            </p>
 
-			<h3>
-				<Text id="app.settings.pages.appearance.emoji_pack" />
-			</h3>
-			<div className={styles.emojiPack}>
-				<div className={styles.row}>
-					<div>
-						<div
-							className={styles.button}
-							onClick={() => setEmojiPack("mutant")}
-							data-active={emojiPack === "mutant"}>
-							<img src={mutantSVG} draggable={false} />
-						</div>
-						<h4>
-							Mutant Remix{" "}
-							<a
-								href="https://mutant.revolt.chat"
-								target="_blank">
-								(by Revolt)
-							</a>
-						</h4>
-					</div>
-					<div>
-						<div
-							className={styles.button}
-							onClick={() => setEmojiPack("twemoji")}
-							data-active={emojiPack === "twemoji"}>
-							<img src={twemojiSVG} draggable={false} />
-						</div>
-						<h4>Twemoji</h4>
-					</div>
-				</div>
-				<div className={styles.row}>
-					<div>
-						<div
-							className={styles.button}
-							onClick={() => setEmojiPack("openmoji")}
-							data-active={emojiPack === "openmoji"}>
-							<img src={openmojiSVG} draggable={false} />
-						</div>
-						<h4>Openmoji</h4>
-					</div>
-					<div>
-						<div
-							className={styles.button}
-							onClick={() => setEmojiPack("noto")}
-							data-active={emojiPack === "noto"}>
-							<img src={notoSVG} draggable={false} />
-						</div>
-						<h4>Noto Emoji</h4>
-					</div>
-				</div>
-			</div>
+            <h3>
+                <Text id="app.settings.pages.appearance.emoji_pack" />
+            </h3>
+            <div className={styles.emojiPack}>
+                <div className={styles.row}>
+                    <div>
+                        <div
+                            className={styles.button}
+                            onClick={() => setEmojiPack("mutant")}
+                            data-active={emojiPack === "mutant"}>
+                            <img src={mutantSVG} draggable={false} />
+                        </div>
+                        <h4>
+                            Mutant Remix{" "}
+                            <a
+                                href="https://mutant.revolt.chat"
+                                target="_blank">
+                                (by Revolt)
+                            </a>
+                        </h4>
+                    </div>
+                    <div>
+                        <div
+                            className={styles.button}
+                            onClick={() => setEmojiPack("twemoji")}
+                            data-active={emojiPack === "twemoji"}>
+                            <img src={twemojiSVG} draggable={false} />
+                        </div>
+                        <h4>Twemoji</h4>
+                    </div>
+                </div>
+                <div className={styles.row}>
+                    <div>
+                        <div
+                            className={styles.button}
+                            onClick={() => setEmojiPack("openmoji")}
+                            data-active={emojiPack === "openmoji"}>
+                            <img src={openmojiSVG} draggable={false} />
+                        </div>
+                        <h4>Openmoji</h4>
+                    </div>
+                    <div>
+                        <div
+                            className={styles.button}
+                            onClick={() => setEmojiPack("noto")}
+                            data-active={emojiPack === "noto"}>
+                            <img src={notoSVG} draggable={false} />
+                        </div>
+                        <h4>Noto Emoji</h4>
+                    </div>
+                </div>
+            </div>
 
-			<CollapsibleSection
-				id="settings_advanced_appearance"
-				defaultValue={false}
-				summary={<Text id="app.settings.pages.appearance.advanced" />}>
-				<h3>
-					<Text id="app.settings.pages.appearance.overrides" />
-				</h3>
-				<div className={styles.actions}>
-					<Button contrast onClick={() => setTheme({ custom: {} })}>
-						<Text id="app.settings.pages.appearance.reset_overrides" />
-					</Button>
-					<Button
-						contrast
-						onClick={() => writeClipboard(JSON.stringify(theme))}>
-						<Text id="app.settings.pages.appearance.export_clipboard" />
-					</Button>
-					<Button
-						contrast
-						onClick={async () => {
-							const text = await navigator.clipboard.readText();
-							setOverride(JSON.parse(text));
-						}}>
-						<Text id="app.settings.pages.appearance.import_clipboard" />
-					</Button>
-					<Button
-						contrast
-						onClick={async () => {
-							openScreen({
-								id: "_input",
-								question: (
-									<Text id="app.settings.pages.appearance.import_theme" />
-								),
-								field: (
-									<Text id="app.settings.pages.appearance.theme_data" />
-								),
-								callback: async (string) =>
-									setOverride(JSON.parse(string)),
-							});
-						}}>
-						<Text id="app.settings.pages.appearance.import_manual" />
-					</Button>
-				</div>
-				<div className={styles.overrides}>
-					{(
-						[
-							"accent",
-							"background",
-							"foreground",
-							"primary-background",
-							"primary-header",
-							"secondary-background",
-							"secondary-foreground",
-							"secondary-header",
-							"tertiary-background",
-							"tertiary-foreground",
-							"block",
-							"message-box",
-							"mention",
-							"scrollbar-thumb",
-							"scrollbar-track",
-							"status-online",
-							"status-away",
-							"status-busy",
-							"status-streaming",
-							"status-invisible",
-							"success",
-							"warning",
-							"error",
-							"hover",
-						] as const
-					).map((x) => (
-						<div className={styles.entry} key={x}>
-							<span>{x}</span>
-							<div className={styles.override}>
-								<div
-									className={styles.picker}
-									style={{ backgroundColor: theme[x] }}>
-									<input
-										type="color"
-										value={theme[x]}
-										onChange={(v) =>
-											setOverride({
-												[x]: v.currentTarget.value,
-											})
-										}
-									/>
-								</div>
-								<InputBox
-									className={styles.text}
-									value={theme[x]}
-									onChange={(y) =>
-										setOverride({
-											[x]: y.currentTarget.value,
-										})
-									}
-								/>
-							</div>
-						</div>
-					))}
-				</div>
+            <CollapsibleSection
+                id="settings_advanced_appearance"
+                defaultValue={false}
+                summary={<Text id="app.settings.pages.appearance.advanced" />}>
+                <h3>
+                    <Text id="app.settings.pages.appearance.overrides" />
+                </h3>
+                <div className={styles.actions}>
+                    <Button contrast onClick={() => setTheme({ custom: {} })}>
+                        <Text id="app.settings.pages.appearance.reset_overrides" />
+                    </Button>
+                    <Button
+                        contrast
+                        onClick={() => writeClipboard(JSON.stringify(theme))}>
+                        <Text id="app.settings.pages.appearance.export_clipboard" />
+                    </Button>
+                    <Button
+                        contrast
+                        onClick={async () => {
+                            const text = await navigator.clipboard.readText();
+                            setOverride(JSON.parse(text));
+                        }}>
+                        <Text id="app.settings.pages.appearance.import_clipboard" />
+                    </Button>
+                    <Button
+                        contrast
+                        onClick={async () => {
+                            openScreen({
+                                id: "_input",
+                                question: (
+                                    <Text id="app.settings.pages.appearance.import_theme" />
+                                ),
+                                field: (
+                                    <Text id="app.settings.pages.appearance.theme_data" />
+                                ),
+                                callback: async (string) =>
+                                    setOverride(JSON.parse(string)),
+                            });
+                        }}>
+                        <Text id="app.settings.pages.appearance.import_manual" />
+                    </Button>
+                </div>
+                <div className={styles.overrides}>
+                    {(
+                        [
+                            "accent",
+                            "background",
+                            "foreground",
+                            "primary-background",
+                            "primary-header",
+                            "secondary-background",
+                            "secondary-foreground",
+                            "secondary-header",
+                            "tertiary-background",
+                            "tertiary-foreground",
+                            "block",
+                            "message-box",
+                            "mention",
+                            "scrollbar-thumb",
+                            "scrollbar-track",
+                            "status-online",
+                            "status-away",
+                            "status-busy",
+                            "status-streaming",
+                            "status-invisible",
+                            "success",
+                            "warning",
+                            "error",
+                            "hover",
+                        ] as const
+                    ).map((x) => (
+                        <div className={styles.entry} key={x}>
+                            <span>{x}</span>
+                            <div className={styles.override}>
+                                <div
+                                    className={styles.picker}
+                                    style={{ backgroundColor: theme[x] }}>
+                                    <input
+                                        type="color"
+                                        value={theme[x]}
+                                        onChange={(v) =>
+                                            setOverride({
+                                                [x]: v.currentTarget.value,
+                                            })
+                                        }
+                                    />
+                                </div>
+                                <InputBox
+                                    className={styles.text}
+                                    value={theme[x]}
+                                    onChange={(y) =>
+                                        setOverride({
+                                            [x]: y.currentTarget.value,
+                                        })
+                                    }
+                                />
+                            </div>
+                        </div>
+                    ))}
+                </div>
 
-				<h3>
-					<Text id="app.settings.pages.appearance.mono_font" />
-				</h3>
-				<ComboBox
-					value={theme.monoscapeFont ?? DEFAULT_MONO_FONT}
-					onChange={(e) =>
-						setTheme({
-							custom: {
-								monoscapeFont: e.currentTarget.value as any,
-							},
-						})
-					}>
-					{MONOSCAPE_FONT_KEYS.map((key) => (
-						<option value={key}>
-							{
-								MONOSCAPE_FONTS[
-									key as keyof typeof MONOSCAPE_FONTS
-								].name
-							}
-						</option>
-					))}
-				</ComboBox>
+                <h3>
+                    <Text id="app.settings.pages.appearance.mono_font" />
+                </h3>
+                <ComboBox
+                    value={theme.monoscapeFont ?? DEFAULT_MONO_FONT}
+                    onChange={(e) =>
+                        setTheme({
+                            custom: {
+                                monoscapeFont: e.currentTarget.value as any,
+                            },
+                        })
+                    }>
+                    {MONOSCAPE_FONT_KEYS.map((key) => (
+                        <option value={key}>
+                            {
+                                MONOSCAPE_FONTS[
+                                    key as keyof typeof MONOSCAPE_FONTS
+                                ].name
+                            }
+                        </option>
+                    ))}
+                </ComboBox>
 
-				<h3>
-					<Text id="app.settings.pages.appearance.custom_css" />
-				</h3>
-				<TextAreaAutoSize
-					maxRows={20}
-					minHeight={480}
-					code
-					value={css}
-					onChange={(ev) => setCSS(ev.currentTarget.value)}
-				/>
-			</CollapsibleSection>
-		</div>
-	);
+                <h3>
+                    <Text id="app.settings.pages.appearance.custom_css" />
+                </h3>
+                <TextAreaAutoSize
+                    maxRows={20}
+                    minHeight={480}
+                    code
+                    value={css}
+                    onChange={(ev) => setCSS(ev.currentTarget.value)}
+                />
+            </CollapsibleSection>
+        </div>
+    );
 }
 
 export const Appearance = connectState(Component, (state) => {
-	return {
-		settings: state.settings,
-	};
+    return {
+        settings: state.settings,
+    };
 });
diff --git a/src/pages/settings/panes/Experiments.tsx b/src/pages/settings/panes/Experiments.tsx
index 1ecf8c8..3fbfdc4 100644
--- a/src/pages/settings/panes/Experiments.tsx
+++ b/src/pages/settings/panes/Experiments.tsx
@@ -4,52 +4,52 @@ import { Text } from "preact-i18n";
 import { dispatch } from "../../../redux";
 import { connectState } from "../../../redux/connector";
 import {
-	AVAILABLE_EXPERIMENTS,
-	ExperimentOptions,
+    AVAILABLE_EXPERIMENTS,
+    ExperimentOptions,
 } from "../../../redux/reducers/experiments";
 
 import Checkbox from "../../../components/ui/Checkbox";
 
 interface Props {
-	options?: ExperimentOptions;
+    options?: ExperimentOptions;
 }
 
 export function Component(props: Props) {
-	return (
-		<div className={styles.experiments}>
-			<h3>
-				<Text id="app.settings.pages.experiments.features" />
-			</h3>
-			{AVAILABLE_EXPERIMENTS.map((key) => (
-				<Checkbox
-					checked={(props.options?.enabled ?? []).indexOf(key) > -1}
-					onChange={(enabled) =>
-						dispatch({
-							type: enabled
-								? "EXPERIMENTS_ENABLE"
-								: "EXPERIMENTS_DISABLE",
-							key,
-						})
-					}>
-					<Text id={`app.settings.pages.experiments.titles.${key}`} />
-					<p>
-						<Text
-							id={`app.settings.pages.experiments.descriptions.${key}`}
-						/>
-					</p>
-				</Checkbox>
-			))}
-			{AVAILABLE_EXPERIMENTS.length === 0 && (
-				<div className={styles.empty}>
-					<Text id="app.settings.pages.experiments.not_available" />
-				</div>
-			)}
-		</div>
-	);
+    return (
+        <div className={styles.experiments}>
+            <h3>
+                <Text id="app.settings.pages.experiments.features" />
+            </h3>
+            {AVAILABLE_EXPERIMENTS.map((key) => (
+                <Checkbox
+                    checked={(props.options?.enabled ?? []).indexOf(key) > -1}
+                    onChange={(enabled) =>
+                        dispatch({
+                            type: enabled
+                                ? "EXPERIMENTS_ENABLE"
+                                : "EXPERIMENTS_DISABLE",
+                            key,
+                        })
+                    }>
+                    <Text id={`app.settings.pages.experiments.titles.${key}`} />
+                    <p>
+                        <Text
+                            id={`app.settings.pages.experiments.descriptions.${key}`}
+                        />
+                    </p>
+                </Checkbox>
+            ))}
+            {AVAILABLE_EXPERIMENTS.length === 0 && (
+                <div className={styles.empty}>
+                    <Text id="app.settings.pages.experiments.not_available" />
+                </div>
+            )}
+        </div>
+    );
 }
 
 export const ExperimentsPage = connectState(Component, (state) => {
-	return {
-		options: state.experiments,
-	};
+    return {
+        options: state.experiments,
+    };
 });
diff --git a/src/pages/settings/panes/Feedback.tsx b/src/pages/settings/panes/Feedback.tsx
index fe86dd0..e67ab4b 100644
--- a/src/pages/settings/panes/Feedback.tsx
+++ b/src/pages/settings/panes/Feedback.tsx
@@ -10,101 +10,101 @@ import Radio from "../../../components/ui/Radio";
 import TextArea from "../../../components/ui/TextArea";
 
 export function Feedback() {
-	const user = useSelf();
-	const [other, setOther] = useState("");
-	const [description, setDescription] = useState("");
-	const [state, setState] = useState<"ready" | "sending" | "sent">("ready");
-	const [checked, setChecked] = useState<
-		"Bug" | "Feature Request" | "__other_option__"
-	>("Bug");
+    const user = useSelf();
+    const [other, setOther] = useState("");
+    const [description, setDescription] = useState("");
+    const [state, setState] = useState<"ready" | "sending" | "sent">("ready");
+    const [checked, setChecked] = useState<
+        "Bug" | "Feature Request" | "__other_option__"
+    >("Bug");
 
-	async function onSubmit(ev: JSX.TargetedEvent<HTMLFormElement, Event>) {
-		ev.preventDefault();
-		setState("sending");
+    async function onSubmit(ev: JSX.TargetedEvent<HTMLFormElement, Event>) {
+        ev.preventDefault();
+        setState("sending");
 
-		await fetch(`https://workers.revolt.chat/feedback`, {
-			method: "POST",
-			body: JSON.stringify({
-				checked,
-				other,
-				description,
-				name: user?.username ?? "Unknown User",
-			}),
-			mode: "no-cors",
-		});
+        await fetch(`https://workers.revolt.chat/feedback`, {
+            method: "POST",
+            body: JSON.stringify({
+                checked,
+                other,
+                description,
+                name: user?.username ?? "Unknown User",
+            }),
+            mode: "no-cors",
+        });
 
-		setState("sent");
-		setChecked("Bug");
-		setDescription("");
-		setOther("");
-	}
+        setState("sent");
+        setChecked("Bug");
+        setDescription("");
+        setOther("");
+    }
 
-	return (
-		<form className={styles.feedback} onSubmit={onSubmit}>
-			<h3>
-				<Text id="app.settings.pages.feedback.report" />
-			</h3>
-			<div className={styles.options}>
-				<Radio
-					checked={checked === "Bug"}
-					disabled={state === "sending"}
-					onSelect={() => setChecked("Bug")}>
-					<Text id="app.settings.pages.feedback.bug" />
-				</Radio>
-				<Radio
-					disabled={state === "sending"}
-					checked={checked === "Feature Request"}
-					onSelect={() => setChecked("Feature Request")}>
-					<Text id="app.settings.pages.feedback.feature" />
-				</Radio>
-				{(location.hostname === "vite.revolt.chat" ||
-					location.hostname === "local.revolt.chat") && (
-					<Radio
-						disabled={state === "sending"}
-						checked={other === "Revite"}
-						onSelect={() => {
-							setChecked("__other_option__");
-							setOther("Revite");
-						}}>
-						Issues with Revite
-					</Radio>
-				)}
-				<Radio
-					disabled={state === "sending"}
-					checked={
-						checked === "__other_option__" && other !== "Revite"
-					}
-					onSelect={() => setChecked("__other_option__")}>
-					<Localizer>
-						<InputBox
-							value={other}
-							disabled={state === "sending"}
-							name="entry.1151440373.other_option_response"
-							onChange={(e) => setOther(e.currentTarget.value)}
-							placeholder={
-								(
-									<Text id="app.settings.pages.feedback.other" />
-								) as any
-							}
-						/>
-					</Localizer>
-				</Radio>
-			</div>
-			<h3>
-				<Text id="app.settings.pages.feedback.describe" />
-			</h3>
-			<TextArea
-				// maxRows={10}
-				value={description}
-				id="entry.685672624"
-				disabled={state === "sending"}
-				onChange={(ev) => setDescription(ev.currentTarget.value)}
-			/>
-			<p>
-				<Button type="submit" contrast>
-					<Text id="app.settings.pages.feedback.send" />
-				</Button>
-			</p>
-		</form>
-	);
+    return (
+        <form className={styles.feedback} onSubmit={onSubmit}>
+            <h3>
+                <Text id="app.settings.pages.feedback.report" />
+            </h3>
+            <div className={styles.options}>
+                <Radio
+                    checked={checked === "Bug"}
+                    disabled={state === "sending"}
+                    onSelect={() => setChecked("Bug")}>
+                    <Text id="app.settings.pages.feedback.bug" />
+                </Radio>
+                <Radio
+                    disabled={state === "sending"}
+                    checked={checked === "Feature Request"}
+                    onSelect={() => setChecked("Feature Request")}>
+                    <Text id="app.settings.pages.feedback.feature" />
+                </Radio>
+                {(location.hostname === "vite.revolt.chat" ||
+                    location.hostname === "local.revolt.chat") && (
+                    <Radio
+                        disabled={state === "sending"}
+                        checked={other === "Revite"}
+                        onSelect={() => {
+                            setChecked("__other_option__");
+                            setOther("Revite");
+                        }}>
+                        Issues with Revite
+                    </Radio>
+                )}
+                <Radio
+                    disabled={state === "sending"}
+                    checked={
+                        checked === "__other_option__" && other !== "Revite"
+                    }
+                    onSelect={() => setChecked("__other_option__")}>
+                    <Localizer>
+                        <InputBox
+                            value={other}
+                            disabled={state === "sending"}
+                            name="entry.1151440373.other_option_response"
+                            onChange={(e) => setOther(e.currentTarget.value)}
+                            placeholder={
+                                (
+                                    <Text id="app.settings.pages.feedback.other" />
+                                ) as any
+                            }
+                        />
+                    </Localizer>
+                </Radio>
+            </div>
+            <h3>
+                <Text id="app.settings.pages.feedback.describe" />
+            </h3>
+            <TextArea
+                // maxRows={10}
+                value={description}
+                id="entry.685672624"
+                disabled={state === "sending"}
+                onChange={(ev) => setDescription(ev.currentTarget.value)}
+            />
+            <p>
+                <Button type="submit" contrast>
+                    <Text id="app.settings.pages.feedback.send" />
+                </Button>
+            </p>
+        </form>
+    );
 }
diff --git a/src/pages/settings/panes/Languages.tsx b/src/pages/settings/panes/Languages.tsx
index 772795c..559ccc0 100644
--- a/src/pages/settings/panes/Languages.tsx
+++ b/src/pages/settings/panes/Languages.tsx
@@ -5,9 +5,9 @@ import { dispatch } from "../../../redux";
 import { connectState } from "../../../redux/connector";
 
 import {
-	Language,
-	LanguageEntry,
-	Languages as Langs,
+    Language,
+    LanguageEntry,
+    Languages as Langs,
 } from "../../../context/Locale";
 
 import Emoji from "../../../components/common/Emoji";
@@ -15,77 +15,77 @@ import Checkbox from "../../../components/ui/Checkbox";
 import Tip from "../../../components/ui/Tip";
 
 type Props = {
-	locale: Language;
+    locale: Language;
 };
 
 type Key = [string, LanguageEntry];
 
 function Entry({ entry: [x, lang], locale }: { entry: Key } & Props) {
-	return (
-		<Checkbox
-			key={x}
-			className={styles.entry}
-			checked={locale === x}
-			onChange={(v) => {
-				if (v) {
-					dispatch({
-						type: "SET_LOCALE",
-						locale: x as Language,
-					});
-				}
-			}}>
-			<div className={styles.flag}>
-				<Emoji size={42} emoji={lang.emoji} />
-			</div>
-			<span className={styles.description}>{lang.display}</span>
-		</Checkbox>
-	);
+    return (
+        <Checkbox
+            key={x}
+            className={styles.entry}
+            checked={locale === x}
+            onChange={(v) => {
+                if (v) {
+                    dispatch({
+                        type: "SET_LOCALE",
+                        locale: x as Language,
+                    });
+                }
+            }}>
+            <div className={styles.flag}>
+                <Emoji size={42} emoji={lang.emoji} />
+            </div>
+            <span className={styles.description}>{lang.display}</span>
+        </Checkbox>
+    );
 }
 
 export function Component(props: Props) {
-	const languages = Object.keys(Langs).map((x) => [
-		x,
-		Langs[x as keyof typeof Langs],
-	]) as Key[];
+    const languages = Object.keys(Langs).map((x) => [
+        x,
+        Langs[x as keyof typeof Langs],
+    ]) as Key[];
 
-	return (
-		<div className={styles.languages}>
-			<h3>
-				<Text id="app.settings.pages.language.select" />
-			</h3>
-			<div className={styles.list}>
-				{languages
-					.filter(([, lang]) => !lang.alt)
-					.map(([x, lang]) => (
-						<Entry key={x} entry={[x, lang]} {...props} />
-					))}
-			</div>
-			<h3>
-				<Text id="app.settings.pages.language.other" />
-			</h3>
-			<div className={styles.list}>
-				{languages
-					.filter(([, lang]) => lang.alt)
-					.map(([x, lang]) => (
-						<Entry key={x} entry={[x, lang]} {...props} />
-					))}
-			</div>
-			<Tip>
-				<span>
-					<Text id="app.settings.tips.languages.a" />
-				</span>{" "}
-				<a
-					href="https://weblate.insrt.uk/engage/revolt/?utm_source=widget"
-					target="_blank">
-					<Text id="app.settings.tips.languages.b" />
-				</a>
-			</Tip>
-		</div>
-	);
+    return (
+        <div className={styles.languages}>
+            <h3>
+                <Text id="app.settings.pages.language.select" />
+            </h3>
+            <div className={styles.list}>
+                {languages
+                    .filter(([, lang]) => !lang.alt)
+                    .map(([x, lang]) => (
+                        <Entry key={x} entry={[x, lang]} {...props} />
+                    ))}
+            </div>
+            <h3>
+                <Text id="app.settings.pages.language.other" />
+            </h3>
+            <div className={styles.list}>
+                {languages
+                    .filter(([, lang]) => lang.alt)
+                    .map(([x, lang]) => (
+                        <Entry key={x} entry={[x, lang]} {...props} />
+                    ))}
+            </div>
+            <Tip>
+                <span>
+                    <Text id="app.settings.tips.languages.a" />
+                </span>{" "}
+                <a
+                    href="https://weblate.insrt.uk/engage/revolt/?utm_source=widget"
+                    target="_blank">
+                    <Text id="app.settings.tips.languages.b" />
+                </a>
+            </Tip>
+        </div>
+    );
 }
 
 export const Languages = connectState(Component, (state) => {
-	return {
-		locale: state.locale,
-	};
+    return {
+        locale: state.locale,
+    };
 });
diff --git a/src/pages/settings/panes/Notifications.tsx b/src/pages/settings/panes/Notifications.tsx
index af26673..a7a032d 100644
--- a/src/pages/settings/panes/Notifications.tsx
+++ b/src/pages/settings/panes/Notifications.tsx
@@ -9,9 +9,9 @@ import { urlBase64ToUint8Array } from "../../../lib/conversion";
 import { dispatch } from "../../../redux";
 import { connectState } from "../../../redux/connector";
 import {
-	DEFAULT_SOUNDS,
-	NotificationOptions,
-	SoundOptions,
+    DEFAULT_SOUNDS,
+    NotificationOptions,
+    SoundOptions,
 } from "../../../redux/reducers/settings";
 
 import { useIntermediate } from "../../../context/intermediate/Intermediate";
@@ -22,133 +22,133 @@ import Checkbox from "../../../components/ui/Checkbox";
 import { SOUNDS_ARRAY } from "../../../assets/sounds/Audio";
 
 interface Props {
-	options?: NotificationOptions;
+    options?: NotificationOptions;
 }
 
 export function Component({ options }: Props) {
-	const client = useContext(AppContext);
-	const { openScreen } = useIntermediate();
-	const [pushEnabled, setPushEnabled] = useState<undefined | boolean>(
-		undefined,
-	);
+    const client = useContext(AppContext);
+    const { openScreen } = useIntermediate();
+    const [pushEnabled, setPushEnabled] = useState<undefined | boolean>(
+        undefined,
+    );
 
-	// Load current state of pushManager.
-	useEffect(() => {
-		navigator.serviceWorker
-			?.getRegistration()
-			.then(async (registration) => {
-				const sub = await registration?.pushManager?.getSubscription();
-				setPushEnabled(sub !== null && sub !== undefined);
-			});
-	}, []);
+    // Load current state of pushManager.
+    useEffect(() => {
+        navigator.serviceWorker
+            ?.getRegistration()
+            .then(async (registration) => {
+                const sub = await registration?.pushManager?.getSubscription();
+                setPushEnabled(sub !== null && sub !== undefined);
+            });
+    }, []);
 
-	const enabledSounds: SoundOptions = defaultsDeep(
-		options?.sounds ?? {},
-		DEFAULT_SOUNDS,
-	);
-	return (
-		<div className={styles.notifications}>
-			<h3>
-				<Text id="app.settings.pages.notifications.push_notifications" />
-			</h3>
-			<Checkbox
-				disabled={!("Notification" in window)}
-				checked={options?.desktopEnabled ?? false}
-				description={
-					<Text id="app.settings.pages.notifications.descriptions.enable_desktop" />
-				}
-				onChange={async (desktopEnabled) => {
-					if (desktopEnabled) {
-						let permission = await Notification.requestPermission();
-						if (permission !== "granted") {
-							return openScreen({
-								id: "error",
-								error: "DeniedNotification",
-							});
-						}
-					}
+    const enabledSounds: SoundOptions = defaultsDeep(
+        options?.sounds ?? {},
+        DEFAULT_SOUNDS,
+    );
+    return (
+        <div className={styles.notifications}>
+            <h3>
+                <Text id="app.settings.pages.notifications.push_notifications" />
+            </h3>
+            <Checkbox
+                disabled={!("Notification" in window)}
+                checked={options?.desktopEnabled ?? false}
+                description={
+                    <Text id="app.settings.pages.notifications.descriptions.enable_desktop" />
+                }
+                onChange={async (desktopEnabled) => {
+                    if (desktopEnabled) {
+                        let permission = await Notification.requestPermission();
+                        if (permission !== "granted") {
+                            return openScreen({
+                                id: "error",
+                                error: "DeniedNotification",
+                            });
+                        }
+                    }
 
-					dispatch({
-						type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
-						options: { desktopEnabled },
-					});
-				}}>
-				<Text id="app.settings.pages.notifications.enable_desktop" />
-			</Checkbox>
-			<Checkbox
-				disabled={typeof pushEnabled === "undefined"}
-				checked={pushEnabled ?? false}
-				description={
-					<Text id="app.settings.pages.notifications.descriptions.enable_push" />
-				}
-				onChange={async (pushEnabled) => {
-					try {
-						const reg =
-							await navigator.serviceWorker?.getRegistration();
-						if (reg) {
-							if (pushEnabled) {
-								const sub = await reg.pushManager.subscribe({
-									userVisibleOnly: true,
-									applicationServerKey: urlBase64ToUint8Array(
-										client.configuration!.vapid,
-									),
-								});
+                    dispatch({
+                        type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
+                        options: { desktopEnabled },
+                    });
+                }}>
+                <Text id="app.settings.pages.notifications.enable_desktop" />
+            </Checkbox>
+            <Checkbox
+                disabled={typeof pushEnabled === "undefined"}
+                checked={pushEnabled ?? false}
+                description={
+                    <Text id="app.settings.pages.notifications.descriptions.enable_push" />
+                }
+                onChange={async (pushEnabled) => {
+                    try {
+                        const reg =
+                            await navigator.serviceWorker?.getRegistration();
+                        if (reg) {
+                            if (pushEnabled) {
+                                const sub = await reg.pushManager.subscribe({
+                                    userVisibleOnly: true,
+                                    applicationServerKey: urlBase64ToUint8Array(
+                                        client.configuration!.vapid,
+                                    ),
+                                });
 
-								// tell the server we just subscribed
-								const json = sub.toJSON();
-								if (json.keys) {
-									client.req("POST", "/push/subscribe", {
-										endpoint: sub.endpoint,
-										...(json.keys as {
-											p256dh: string;
-											auth: string;
-										}),
-									});
-									setPushEnabled(true);
-								}
-							} else {
-								const sub =
-									await reg.pushManager.getSubscription();
-								sub?.unsubscribe();
-								setPushEnabled(false);
+                                // tell the server we just subscribed
+                                const json = sub.toJSON();
+                                if (json.keys) {
+                                    client.req("POST", "/push/subscribe", {
+                                        endpoint: sub.endpoint,
+                                        ...(json.keys as {
+                                            p256dh: string;
+                                            auth: string;
+                                        }),
+                                    });
+                                    setPushEnabled(true);
+                                }
+                            } else {
+                                const sub =
+                                    await reg.pushManager.getSubscription();
+                                sub?.unsubscribe();
+                                setPushEnabled(false);
 
-								client.req("POST", "/push/unsubscribe");
-							}
-						}
-					} catch (err) {
-						console.error("Failed to enable push!", err);
-					}
-				}}>
-				<Text id="app.settings.pages.notifications.enable_push" />
-			</Checkbox>
-			<h3>
-				<Text id="app.settings.pages.notifications.sounds" />
-			</h3>
-			{SOUNDS_ARRAY.map((key) => (
-				<Checkbox
-					checked={enabledSounds[key] ? true : false}
-					onChange={(enabled) =>
-						dispatch({
-							type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
-							options: {
-								sounds: {
-									...options?.sounds,
-									[key]: enabled,
-								},
-							},
-						})
-					}>
-					<Text
-						id={`app.settings.pages.notifications.sound.${key}`}
-					/>
-				</Checkbox>
-			))}
-		</div>
-	);
+                                client.req("POST", "/push/unsubscribe");
+                            }
+                        }
+                    } catch (err) {
+                        console.error("Failed to enable push!", err);
+                    }
+                }}>
+                <Text id="app.settings.pages.notifications.enable_push" />
+            </Checkbox>
+            <h3>
+                <Text id="app.settings.pages.notifications.sounds" />
+            </h3>
+            {SOUNDS_ARRAY.map((key) => (
+                <Checkbox
+                    checked={enabledSounds[key] ? true : false}
+                    onChange={(enabled) =>
+                        dispatch({
+                            type: "SETTINGS_SET_NOTIFICATION_OPTIONS",
+                            options: {
+                                sounds: {
+                                    ...options?.sounds,
+                                    [key]: enabled,
+                                },
+                            },
+                        })
+                    }>
+                    <Text
+                        id={`app.settings.pages.notifications.sound.${key}`}
+                    />
+                </Checkbox>
+            ))}
+        </div>
+    );
 }
 
 export const Notifications = connectState(Component, (state) => {
-	return {
-		options: state.settings.notification,
-	};
+    return {
+        options: state.settings.notification,
+    };
 });
diff --git a/src/pages/settings/panes/Profile.tsx b/src/pages/settings/panes/Profile.tsx
index 6e1fd4d..b9fe3a2 100644
--- a/src/pages/settings/panes/Profile.tsx
+++ b/src/pages/settings/panes/Profile.tsx
@@ -9,178 +9,178 @@ import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
 import { UserProfile } from "../../../context/intermediate/popovers/UserProfile";
 import { FileUploader } from "../../../context/revoltjs/FileUploads";
 import {
-	ClientStatus,
-	StatusContext,
+    ClientStatus,
+    StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
 import { useForceUpdate, useSelf } from "../../../context/revoltjs/hooks";
 
 import AutoComplete, {
-	useAutoComplete,
+    useAutoComplete,
 } from "../../../components/common/AutoComplete";
 import Button from "../../../components/ui/Button";
 
 export function Profile() {
-	const { intl } = useContext(IntlContext);
-	const status = useContext(StatusContext);
+    const { intl } = useContext(IntlContext);
+    const status = useContext(StatusContext);
 
-	const ctx = useForceUpdate();
-	const user = useSelf();
-	if (!user) return null;
+    const ctx = useForceUpdate();
+    const user = useSelf();
+    if (!user) return null;
 
-	const [profile, setProfile] = useState<undefined | Users.Profile>(
-		undefined,
-	);
+    const [profile, setProfile] = useState<undefined | Users.Profile>(
+        undefined,
+    );
 
-	// ! FIXME: temporary solution
-	// ! we should just announce profile changes through WS
-	function refreshProfile() {
-		ctx.client.users
-			.fetchProfile(user!._id)
-			.then((profile) => setProfile(profile ?? {}));
-	}
+    // ! FIXME: temporary solution
+    // ! we should just announce profile changes through WS
+    function refreshProfile() {
+        ctx.client.users
+            .fetchProfile(user!._id)
+            .then((profile) => setProfile(profile ?? {}));
+    }
 
-	useEffect(() => {
-		if (profile === undefined && status === ClientStatus.ONLINE) {
-			refreshProfile();
-		}
-	}, [status]);
+    useEffect(() => {
+        if (profile === undefined && status === ClientStatus.ONLINE) {
+            refreshProfile();
+        }
+    }, [status]);
 
-	const [changed, setChanged] = useState(false);
-	function setContent(content?: string) {
-		setProfile({ ...profile, content });
-		if (!changed) setChanged(true);
-	}
+    const [changed, setChanged] = useState(false);
+    function setContent(content?: string) {
+        setProfile({ ...profile, content });
+        if (!changed) setChanged(true);
+    }
 
-	const {
-		onChange,
-		onKeyUp,
-		onKeyDown,
-		onFocus,
-		onBlur,
-		...autoCompleteProps
-	} = useAutoComplete(setContent, {
-		users: { type: "all" },
-	});
+    const {
+        onChange,
+        onKeyUp,
+        onKeyDown,
+        onFocus,
+        onBlur,
+        ...autoCompleteProps
+    } = useAutoComplete(setContent, {
+        users: { type: "all" },
+    });
 
-	return (
-		<div className={styles.user}>
-			<h3>
-				<Text id="app.special.modals.actions.preview" />
-			</h3>
-			<div className={styles.preview}>
-				<UserProfile
-					user_id={user._id}
-					dummy={true}
-					dummyProfile={profile}
-					onClose={() => {}}
-				/>
-			</div>
-			<div className={styles.row}>
-				<div className={styles.pfp}>
-					<h3>
-						<Text id="app.settings.pages.profile.profile_picture" />
-					</h3>
-					<FileUploader
-						width={80}
-						height={80}
-						style="icon"
-						fileType="avatars"
-						behaviour="upload"
-						maxFileSize={4_000_000}
-						onUpload={(avatar) =>
-							ctx.client.users.editUser({ avatar })
-						}
-						remove={() =>
-							ctx.client.users.editUser({ remove: "Avatar" })
-						}
-						defaultPreview={ctx.client.users.getAvatarURL(
-							user._id,
-							{ max_side: 256 },
-							true,
-						)}
-						previewURL={ctx.client.users.getAvatarURL(
-							user._id,
-							{ max_side: 256 },
-							true,
-							true,
-						)}
-					/>
-				</div>
-				<div className={styles.background}>
-					<h3>
-						<Text id="app.settings.pages.profile.custom_background" />
-					</h3>
-					<FileUploader
-						height={92}
-						style="banner"
-						behaviour="upload"
-						fileType="backgrounds"
-						maxFileSize={6_000_000}
-						onUpload={async (background) => {
-							await ctx.client.users.editUser({
-								profile: { background },
-							});
-							refreshProfile();
-						}}
-						remove={async () => {
-							await ctx.client.users.editUser({
-								remove: "ProfileBackground",
-							});
-							setProfile({ ...profile, background: undefined });
-						}}
-						previewURL={
-							profile?.background
-								? ctx.client.users.getBackgroundURL(
-										profile,
-										{ width: 1000 },
-										true,
-								  )
-								: undefined
-						}
-					/>
-				</div>
-			</div>
-			<h3>
-				<Text id="app.settings.pages.profile.info" />
-			</h3>
-			<AutoComplete detached {...autoCompleteProps} />
-			<TextAreaAutoSize
-				maxRows={10}
-				minHeight={200}
-				maxLength={2000}
-				value={profile?.content ?? ""}
-				disabled={typeof profile === "undefined"}
-				onChange={(ev) => {
-					onChange(ev);
-					setContent(ev.currentTarget.value);
-				}}
-				placeholder={translate(
-					`app.settings.pages.profile.${
-						typeof profile === "undefined"
-							? "fetching"
-							: "placeholder"
-					}`,
-					"",
-					(intl as any).dictionary as Record<string, unknown>,
-				)}
-				onKeyUp={onKeyUp}
-				onKeyDown={onKeyDown}
-				onFocus={onFocus}
-				onBlur={onBlur}
-			/>
-			<p>
-				<Button
-					contrast
-					onClick={() => {
-						setChanged(false);
-						ctx.client.users.editUser({
-							profile: { content: profile?.content },
-						});
-					}}
-					disabled={!changed}>
-					<Text id="app.special.modals.actions.save" />
-				</Button>
-			</p>
-		</div>
-	);
+    return (
+        <div className={styles.user}>
+            <h3>
+                <Text id="app.special.modals.actions.preview" />
+            </h3>
+            <div className={styles.preview}>
+                <UserProfile
+                    user_id={user._id}
+                    dummy={true}
+                    dummyProfile={profile}
+                    onClose={() => {}}
+                />
+            </div>
+            <div className={styles.row}>
+                <div className={styles.pfp}>
+                    <h3>
+                        <Text id="app.settings.pages.profile.profile_picture" />
+                    </h3>
+                    <FileUploader
+                        width={80}
+                        height={80}
+                        style="icon"
+                        fileType="avatars"
+                        behaviour="upload"
+                        maxFileSize={4_000_000}
+                        onUpload={(avatar) =>
+                            ctx.client.users.editUser({ avatar })
+                        }
+                        remove={() =>
+                            ctx.client.users.editUser({ remove: "Avatar" })
+                        }
+                        defaultPreview={ctx.client.users.getAvatarURL(
+                            user._id,
+                            { max_side: 256 },
+                            true,
+                        )}
+                        previewURL={ctx.client.users.getAvatarURL(
+                            user._id,
+                            { max_side: 256 },
+                            true,
+                            true,
+                        )}
+                    />
+                </div>
+                <div className={styles.background}>
+                    <h3>
+                        <Text id="app.settings.pages.profile.custom_background" />
+                    </h3>
+                    <FileUploader
+                        height={92}
+                        style="banner"
+                        behaviour="upload"
+                        fileType="backgrounds"
+                        maxFileSize={6_000_000}
+                        onUpload={async (background) => {
+                            await ctx.client.users.editUser({
+                                profile: { background },
+                            });
+                            refreshProfile();
+                        }}
+                        remove={async () => {
+                            await ctx.client.users.editUser({
+                                remove: "ProfileBackground",
+                            });
+                            setProfile({ ...profile, background: undefined });
+                        }}
+                        previewURL={
+                            profile?.background
+                                ? ctx.client.users.getBackgroundURL(
+                                      profile,
+                                      { width: 1000 },
+                                      true,
+                                  )
+                                : undefined
+                        }
+                    />
+                </div>
+            </div>
+            <h3>
+                <Text id="app.settings.pages.profile.info" />
+            </h3>
+            <AutoComplete detached {...autoCompleteProps} />
+            <TextAreaAutoSize
+                maxRows={10}
+                minHeight={200}
+                maxLength={2000}
+                value={profile?.content ?? ""}
+                disabled={typeof profile === "undefined"}
+                onChange={(ev) => {
+                    onChange(ev);
+                    setContent(ev.currentTarget.value);
+                }}
+                placeholder={translate(
+                    `app.settings.pages.profile.${
+                        typeof profile === "undefined"
+                            ? "fetching"
+                            : "placeholder"
+                    }`,
+                    "",
+                    (intl as any).dictionary as Record<string, unknown>,
+                )}
+                onKeyUp={onKeyUp}
+                onKeyDown={onKeyDown}
+                onFocus={onFocus}
+                onBlur={onBlur}
+            />
+            <p>
+                <Button
+                    contrast
+                    onClick={() => {
+                        setChanged(false);
+                        ctx.client.users.editUser({
+                            profile: { content: profile?.content },
+                        });
+                    }}
+                    disabled={!changed}>
+                    <Text id="app.special.modals.actions.save" />
+                </Button>
+            </p>
+        </div>
+    );
 }
diff --git a/src/pages/settings/panes/Sessions.tsx b/src/pages/settings/panes/Sessions.tsx
index 38a8557..e543da1 100644
--- a/src/pages/settings/panes/Sessions.tsx
+++ b/src/pages/settings/panes/Sessions.tsx
@@ -1,14 +1,14 @@
 import { HelpCircle } from "@styled-icons/boxicons-regular";
 import {
-	Android,
-	Firefoxbrowser,
-	Googlechrome,
-	Ios,
-	Linux,
-	Macos,
-	Microsoftedge,
-	Safari,
-	Windows,
+    Android,
+    Firefoxbrowser,
+    Googlechrome,
+    Ios,
+    Linux,
+    Macos,
+    Microsoftedge,
+    Safari,
+    Windows,
 } from "@styled-icons/simple-icons";
 import dayjs from "dayjs";
 import relativeTime from "dayjs/plugin/relativeTime";
@@ -28,159 +28,159 @@ import Tip from "../../../components/ui/Tip";
 dayjs.extend(relativeTime);
 
 interface Session {
-	id: string;
-	friendly_name: string;
+    id: string;
+    friendly_name: string;
 }
 
 export function Sessions() {
-	const client = useContext(AppContext);
-	const deviceId = client.session?.id;
-
-	const [sessions, setSessions] = useState<Session[] | undefined>(undefined);
-	const [attemptingDelete, setDelete] = useState<string[]>([]);
-	const history = useHistory();
-
-	function switchPage(to: string) {
-		history.replace(`/settings/${to}`);
-	}
-
-	useEffect(() => {
-		client.req("GET", "/auth/sessions").then((data) => {
-			data.sort(
-				(a, b) =>
-					(b.id === deviceId ? 1 : 0) - (a.id === deviceId ? 1 : 0),
-			);
-			setSessions(data);
-		});
-	}, []);
-
-	if (typeof sessions === "undefined") {
-		return (
-			<div className={styles.loader}>
-				<Preloader type="ring" />
-			</div>
-		);
-	}
-
-	function getIcon(session: Session) {
-		const name = session.friendly_name;
-		switch (true) {
-			case /firefox/i.test(name):
-				return <Firefoxbrowser />;
-			case /chrome/i.test(name):
-				return <Googlechrome />;
-			case /safari/i.test(name):
-				return <Safari />;
-			case /edge/i.test(name):
-				return <Microsoftedge />;
-			default:
-				return <HelpCircle />;
-		}
-	}
-
-	function getSystemIcon(session: Session) {
-		const name = session.friendly_name;
-		switch (true) {
-			case /linux/i.test(name):
-				return <Linux />;
-			case /android/i.test(name):
-				return <Android />;
-			case /mac.*os/i.test(name):
-				return <Macos />;
-			case /ios/i.test(name):
-				return <Ios />;
-			case /windows/i.test(name):
-				return <Windows />;
-			default:
-				return null;
-		}
-	}
-
-	const mapped = sessions.map((session) => {
-		return {
-			...session,
-			timestamp: decodeTime(session.id),
-		};
-	});
-
-	mapped.sort((a, b) => b.timestamp - a.timestamp);
-	let id = mapped.findIndex((x) => x.id === deviceId);
-
-	const render = [
-		mapped[id],
-		...mapped.slice(0, id),
-		...mapped.slice(id + 1, mapped.length),
-	];
-
-	return (
-		<div className={styles.sessions}>
-			<h3>
-				<Text id="app.settings.pages.sessions.active_sessions" />
-			</h3>
-			{render.map((session) => (
-				<div
-					className={styles.entry}
-					data-active={session.id === deviceId}
-					data-deleting={attemptingDelete.indexOf(session.id) > -1}>
-					{deviceId === session.id && (
-						<span className={styles.label}>
-							<Text id="app.settings.pages.sessions.this_device" />{" "}
-						</span>
-					)}
-					<div className={styles.session}>
-						<div className={styles.icon}>
-							{getIcon(session)}
-							<div>{getSystemIcon(session)}</div>
-						</div>
-						<div className={styles.info}>
-							<span className={styles.name}>
-								{session.friendly_name}
-							</span>
-							<span className={styles.time}>
-								<Text
-									id="app.settings.pages.sessions.created"
-									fields={{
-										time_ago: dayjs(
-											session.timestamp,
-										).fromNow(),
-									}}
-								/>
-							</span>
-						</div>
-						{deviceId !== session.id && (
-							<Button
-								onClick={async () => {
-									setDelete([
-										...attemptingDelete,
-										session.id,
-									]);
-									await client.req(
-										"DELETE",
-										`/auth/sessions/${session.id}` as "/auth/sessions",
-									);
-									setSessions(
-										sessions?.filter(
-											(x) => x.id !== session.id,
-										),
-									);
-								}}
-								disabled={
-									attemptingDelete.indexOf(session.id) > -1
-								}>
-								<Text id="app.settings.pages.logOut" />
-							</Button>
-						)}
-					</div>
-				</div>
-			))}
-			<Tip>
-				<span>
-					<Text id="app.settings.tips.sessions.a" />
-				</span>{" "}
-				<a onClick={() => switchPage("account")}>
-					<Text id="app.settings.tips.sessions.b" />
-				</a>
-			</Tip>
-		</div>
-	);
+    const client = useContext(AppContext);
+    const deviceId = client.session?.id;
+
+    const [sessions, setSessions] = useState<Session[] | undefined>(undefined);
+    const [attemptingDelete, setDelete] = useState<string[]>([]);
+    const history = useHistory();
+
+    function switchPage(to: string) {
+        history.replace(`/settings/${to}`);
+    }
+
+    useEffect(() => {
+        client.req("GET", "/auth/sessions").then((data) => {
+            data.sort(
+                (a, b) =>
+                    (b.id === deviceId ? 1 : 0) - (a.id === deviceId ? 1 : 0),
+            );
+            setSessions(data);
+        });
+    }, []);
+
+    if (typeof sessions === "undefined") {
+        return (
+            <div className={styles.loader}>
+                <Preloader type="ring" />
+            </div>
+        );
+    }
+
+    function getIcon(session: Session) {
+        const name = session.friendly_name;
+        switch (true) {
+            case /firefox/i.test(name):
+                return <Firefoxbrowser />;
+            case /chrome/i.test(name):
+                return <Googlechrome />;
+            case /safari/i.test(name):
+                return <Safari />;
+            case /edge/i.test(name):
+                return <Microsoftedge />;
+            default:
+                return <HelpCircle />;
+        }
+    }
+
+    function getSystemIcon(session: Session) {
+        const name = session.friendly_name;
+        switch (true) {
+            case /linux/i.test(name):
+                return <Linux />;
+            case /android/i.test(name):
+                return <Android />;
+            case /mac.*os/i.test(name):
+                return <Macos />;
+            case /ios/i.test(name):
+                return <Ios />;
+            case /windows/i.test(name):
+                return <Windows />;
+            default:
+                return null;
+        }
+    }
+
+    const mapped = sessions.map((session) => {
+        return {
+            ...session,
+            timestamp: decodeTime(session.id),
+        };
+    });
+
+    mapped.sort((a, b) => b.timestamp - a.timestamp);
+    let id = mapped.findIndex((x) => x.id === deviceId);
+
+    const render = [
+        mapped[id],
+        ...mapped.slice(0, id),
+        ...mapped.slice(id + 1, mapped.length),
+    ];
+
+    return (
+        <div className={styles.sessions}>
+            <h3>
+                <Text id="app.settings.pages.sessions.active_sessions" />
+            </h3>
+            {render.map((session) => (
+                <div
+                    className={styles.entry}
+                    data-active={session.id === deviceId}
+                    data-deleting={attemptingDelete.indexOf(session.id) > -1}>
+                    {deviceId === session.id && (
+                        <span className={styles.label}>
+                            <Text id="app.settings.pages.sessions.this_device" />{" "}
+                        </span>
+                    )}
+                    <div className={styles.session}>
+                        <div className={styles.icon}>
+                            {getIcon(session)}
+                            <div>{getSystemIcon(session)}</div>
+                        </div>
+                        <div className={styles.info}>
+                            <span className={styles.name}>
+                                {session.friendly_name}
+                            </span>
+                            <span className={styles.time}>
+                                <Text
+                                    id="app.settings.pages.sessions.created"
+                                    fields={{
+                                        time_ago: dayjs(
+                                            session.timestamp,
+                                        ).fromNow(),
+                                    }}
+                                />
+                            </span>
+                        </div>
+                        {deviceId !== session.id && (
+                            <Button
+                                onClick={async () => {
+                                    setDelete([
+                                        ...attemptingDelete,
+                                        session.id,
+                                    ]);
+                                    await client.req(
+                                        "DELETE",
+                                        `/auth/sessions/${session.id}` as "/auth/sessions",
+                                    );
+                                    setSessions(
+                                        sessions?.filter(
+                                            (x) => x.id !== session.id,
+                                        ),
+                                    );
+                                }}
+                                disabled={
+                                    attemptingDelete.indexOf(session.id) > -1
+                                }>
+                                <Text id="app.settings.pages.logOut" />
+                            </Button>
+                        )}
+                    </div>
+                </div>
+            ))}
+            <Tip>
+                <span>
+                    <Text id="app.settings.tips.sessions.a" />
+                </span>{" "}
+                <a onClick={() => switchPage("account")}>
+                    <Text id="app.settings.tips.sessions.b" />
+                </a>
+            </Tip>
+        </div>
+    );
 }
diff --git a/src/pages/settings/panes/Sync.tsx b/src/pages/settings/panes/Sync.tsx
index fec2548..681ab3a 100644
--- a/src/pages/settings/panes/Sync.tsx
+++ b/src/pages/settings/panes/Sync.tsx
@@ -8,49 +8,49 @@ import { SyncKeys, SyncOptions } from "../../../redux/reducers/sync";
 import Checkbox from "../../../components/ui/Checkbox";
 
 interface Props {
-	options?: SyncOptions;
+    options?: SyncOptions;
 }
 
 export function Component(props: Props) {
-	return (
-		<div className={styles.notifications}>
-			<h3>
-				<Text id="app.settings.pages.sync.categories" />
-			</h3>
-			{(
-				[
-					["appearance", "appearance.title"],
-					["theme", "appearance.theme"],
-					["locale", "language.title"],
-					// notifications sync is always-on
-				] as [SyncKeys, string][]
-			).map(([key, title]) => (
-				<Checkbox
-					checked={
-						(props.options?.disabled ?? []).indexOf(key) === -1
-					}
-					description={
-						<Text
-							id={`app.settings.pages.sync.descriptions.${key}`}
-						/>
-					}
-					onChange={(enabled) =>
-						dispatch({
-							type: enabled
-								? "SYNC_ENABLE_KEY"
-								: "SYNC_DISABLE_KEY",
-							key,
-						})
-					}>
-					<Text id={`app.settings.pages.${title}`} />
-				</Checkbox>
-			))}
-		</div>
-	);
+    return (
+        <div className={styles.notifications}>
+            <h3>
+                <Text id="app.settings.pages.sync.categories" />
+            </h3>
+            {(
+                [
+                    ["appearance", "appearance.title"],
+                    ["theme", "appearance.theme"],
+                    ["locale", "language.title"],
+                    // notifications sync is always-on
+                ] as [SyncKeys, string][]
+            ).map(([key, title]) => (
+                <Checkbox
+                    checked={
+                        (props.options?.disabled ?? []).indexOf(key) === -1
+                    }
+                    description={
+                        <Text
+                            id={`app.settings.pages.sync.descriptions.${key}`}
+                        />
+                    }
+                    onChange={(enabled) =>
+                        dispatch({
+                            type: enabled
+                                ? "SYNC_ENABLE_KEY"
+                                : "SYNC_DISABLE_KEY",
+                            key,
+                        })
+                    }>
+                    <Text id={`app.settings.pages.${title}`} />
+                </Checkbox>
+            ))}
+        </div>
+    );
 }
 
 export const Sync = connectState(Component, (state) => {
-	return {
-		options: state.sync,
-	};
+    return {
+        options: state.sync,
+    };
 });
diff --git a/src/pages/settings/server/Bans.tsx b/src/pages/settings/server/Bans.tsx
index 1c92eb8..af369d8 100644
--- a/src/pages/settings/server/Bans.tsx
+++ b/src/pages/settings/server/Bans.tsx
@@ -7,31 +7,31 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
 import Tip from "../../../components/ui/Tip";
 
 interface Props {
-	server: Servers.Server;
+    server: Servers.Server;
 }
 
 export function Bans({ server }: Props) {
-	const client = useContext(AppContext);
-	const [bans, setBans] = useState<Servers.Ban[] | undefined>(undefined);
+    const client = useContext(AppContext);
+    const [bans, setBans] = useState<Servers.Ban[] | undefined>(undefined);
 
-	useEffect(() => {
-		client.servers.fetchBans(server._id).then((bans) => setBans(bans));
-	}, []);
+    useEffect(() => {
+        client.servers.fetchBans(server._id).then((bans) => setBans(bans));
+    }, []);
 
-	return (
-		<div>
-			<Tip warning>This section is under construction.</Tip>
-			{bans?.map((x) => (
-				<div>
-					{x._id.user}: {x.reason ?? "no reason"}{" "}
-					<button
-						onClick={() =>
-							client.servers.unbanUser(server._id, x._id.user)
-						}>
-						unban
-					</button>
-				</div>
-			))}
-		</div>
-	);
+    return (
+        <div>
+            <Tip warning>This section is under construction.</Tip>
+            {bans?.map((x) => (
+                <div>
+                    {x._id.user}: {x.reason ?? "no reason"}{" "}
+                    <button
+                        onClick={() =>
+                            client.servers.unbanUser(server._id, x._id.user)
+                        }>
+                        unban
+                    </button>
+                </div>
+            ))}
+        </div>
+    );
 }
diff --git a/src/pages/settings/server/Invites.tsx b/src/pages/settings/server/Invites.tsx
index 31e9fdd..dfef777 100644
--- a/src/pages/settings/server/Invites.tsx
+++ b/src/pages/settings/server/Invites.tsx
@@ -6,9 +6,9 @@ import { Text } from "preact-i18n";
 import { useEffect, useState } from "preact/hooks";
 
 import {
-	useChannels,
-	useForceUpdate,
-	useUsers,
+    useChannels,
+    useForceUpdate,
+    useUsers,
 } from "../../../context/revoltjs/hooks";
 import { getChannelName } from "../../../context/revoltjs/util";
 
@@ -17,70 +17,70 @@ import IconButton from "../../../components/ui/IconButton";
 import Preloader from "../../../components/ui/Preloader";
 
 interface Props {
-	server: Servers.Server;
+    server: Servers.Server;
 }
 
 export function Invites({ server }: Props) {
-	const [invites, setInvites] = useState<
-		InvitesNS.ServerInvite[] | undefined
-	>(undefined);
+    const [invites, setInvites] = useState<
+        InvitesNS.ServerInvite[] | undefined
+    >(undefined);
 
-	const ctx = useForceUpdate();
-	const [deleting, setDelete] = useState<string[]>([]);
-	const users = useUsers(invites?.map((x) => x.creator) ?? [], ctx);
-	const channels = useChannels(invites?.map((x) => x.channel) ?? [], ctx);
+    const ctx = useForceUpdate();
+    const [deleting, setDelete] = useState<string[]>([]);
+    const users = useUsers(invites?.map((x) => x.creator) ?? [], ctx);
+    const channels = useChannels(invites?.map((x) => x.channel) ?? [], ctx);
 
-	useEffect(() => {
-		ctx.client.servers
-			.fetchInvites(server._id)
-			.then((invites) => setInvites(invites));
-	}, []);
+    useEffect(() => {
+        ctx.client.servers
+            .fetchInvites(server._id)
+            .then((invites) => setInvites(invites));
+    }, []);
 
-	return (
-		<div className={styles.invites}>
-			<div className={styles.subtitle}>
-				<span>Invite Code</span>
-				<span>Invitor</span>
-				<span>Channel</span>
-				<span>Revoke</span>
-			</div>
-			{typeof invites === "undefined" && <Preloader type="ring" />}
-			{invites?.map((invite) => {
-				let creator = users.find((x) => x?._id === invite.creator);
-				let channel = channels.find((x) => x?._id === invite.channel);
+    return (
+        <div className={styles.invites}>
+            <div className={styles.subtitle}>
+                <span>Invite Code</span>
+                <span>Invitor</span>
+                <span>Channel</span>
+                <span>Revoke</span>
+            </div>
+            {typeof invites === "undefined" && <Preloader type="ring" />}
+            {invites?.map((invite) => {
+                let creator = users.find((x) => x?._id === invite.creator);
+                let channel = channels.find((x) => x?._id === invite.channel);
 
-				return (
-					<div
-						className={styles.invite}
-						data-deleting={deleting.indexOf(invite._id) > -1}>
-						<code>{invite._id}</code>
-						<span>
-							<UserIcon target={creator} size={24} />{" "}
-							{creator?.username ?? "unknown"}
-						</span>
-						<span>
-							{channel && creator
-								? getChannelName(ctx.client, channel, true)
-								: "#unknown"}
-						</span>
-						<IconButton
-							onClick={async () => {
-								setDelete([...deleting, invite._id]);
+                return (
+                    <div
+                        className={styles.invite}
+                        data-deleting={deleting.indexOf(invite._id) > -1}>
+                        <code>{invite._id}</code>
+                        <span>
+                            <UserIcon target={creator} size={24} />{" "}
+                            {creator?.username ?? "unknown"}
+                        </span>
+                        <span>
+                            {channel && creator
+                                ? getChannelName(ctx.client, channel, true)
+                                : "#unknown"}
+                        </span>
+                        <IconButton
+                            onClick={async () => {
+                                setDelete([...deleting, invite._id]);
 
-								await ctx.client.deleteInvite(invite._id);
+                                await ctx.client.deleteInvite(invite._id);
 
-								setInvites(
-									invites?.filter(
-										(x) => x._id !== invite._id,
-									),
-								);
-							}}
-							disabled={deleting.indexOf(invite._id) > -1}>
-							<XCircle size={24} />
-						</IconButton>
-					</div>
-				);
-			})}
-		</div>
-	);
+                                setInvites(
+                                    invites?.filter(
+                                        (x) => x._id !== invite._id,
+                                    ),
+                                );
+                            }}
+                            disabled={deleting.indexOf(invite._id) > -1}>
+                            <XCircle size={24} />
+                        </IconButton>
+                    </div>
+                );
+            })}
+        </div>
+    );
 }
diff --git a/src/pages/settings/server/Members.tsx b/src/pages/settings/server/Members.tsx
index faff176..9bfeb9d 100644
--- a/src/pages/settings/server/Members.tsx
+++ b/src/pages/settings/server/Members.tsx
@@ -6,39 +6,39 @@ import { useEffect, useState } from "preact/hooks";
 import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
 
 interface Props {
-	server: Servers.Server;
+    server: Servers.Server;
 }
 
 // ! FIXME: bad code :)
 export function Members({ server }: Props) {
-	const [members, setMembers] = useState<Servers.Member[] | undefined>(
-		undefined,
-	);
+    const [members, setMembers] = useState<Servers.Member[] | undefined>(
+        undefined,
+    );
 
-	const ctx = useForceUpdate();
-	const users = useUsers(members?.map((x) => x._id.user) ?? [], ctx);
+    const ctx = useForceUpdate();
+    const users = useUsers(members?.map((x) => x._id.user) ?? [], ctx);
 
-	useEffect(() => {
-		ctx.client.servers.members
-			.fetchMembers(server._id)
-			.then((members) => setMembers(members));
-	}, []);
+    useEffect(() => {
+        ctx.client.servers.members
+            .fetchMembers(server._id)
+            .then((members) => setMembers(members));
+    }, []);
 
-	return (
-		<div className={styles.members}>
-			<div className={styles.subtitle}>
-				{members?.length ?? 0} Members
-			</div>
-			{members &&
-				members.length > 0 &&
-				users?.map(
-					(x) =>
-						x && (
-							<div className={styles.member}>
-								<div>@{x.username}</div>
-							</div>
-						),
-				)}
-		</div>
-	);
+    return (
+        <div className={styles.members}>
+            <div className={styles.subtitle}>
+                {members?.length ?? 0} Members
+            </div>
+            {members &&
+                members.length > 0 &&
+                users?.map(
+                    (x) =>
+                        x && (
+                            <div className={styles.member}>
+                                <div>@{x.username}</div>
+                            </div>
+                        ),
+                )}
+        </div>
+    );
 }
diff --git a/src/pages/settings/server/Overview.tsx b/src/pages/settings/server/Overview.tsx
index 2f32ee7..3b5ea09 100644
--- a/src/pages/settings/server/Overview.tsx
+++ b/src/pages/settings/server/Overview.tsx
@@ -16,178 +16,178 @@ import ComboBox from "../../../components/ui/ComboBox";
 import InputBox from "../../../components/ui/InputBox";
 
 interface Props {
-	server: Servers.Server;
+    server: Servers.Server;
 }
 
 export function Overview({ server }: Props) {
-	const client = useContext(AppContext);
+    const client = useContext(AppContext);
 
-	const [name, setName] = useState(server.name);
-	const [description, setDescription] = useState(server.description ?? "");
-	const [systemMessages, setSystemMessages] = useState(
-		server.system_messages,
-	);
+    const [name, setName] = useState(server.name);
+    const [description, setDescription] = useState(server.description ?? "");
+    const [systemMessages, setSystemMessages] = useState(
+        server.system_messages,
+    );
 
-	useEffect(() => setName(server.name), [server.name]);
-	useEffect(
-		() => setDescription(server.description ?? ""),
-		[server.description],
-	);
-	useEffect(
-		() => setSystemMessages(server.system_messages),
-		[server.system_messages],
-	);
+    useEffect(() => setName(server.name), [server.name]);
+    useEffect(
+        () => setDescription(server.description ?? ""),
+        [server.description],
+    );
+    useEffect(
+        () => setSystemMessages(server.system_messages),
+        [server.system_messages],
+    );
 
-	const [changed, setChanged] = useState(false);
-	function save() {
-		let changes: Partial<
-			Pick<Servers.Server, "name" | "description" | "system_messages">
-		> = {};
-		if (name !== server.name) changes.name = name;
-		if (description !== server.description)
-			changes.description = description;
-		if (!isEqual(systemMessages, server.system_messages))
-			changes.system_messages = systemMessages;
+    const [changed, setChanged] = useState(false);
+    function save() {
+        let changes: Partial<
+            Pick<Servers.Server, "name" | "description" | "system_messages">
+        > = {};
+        if (name !== server.name) changes.name = name;
+        if (description !== server.description)
+            changes.description = description;
+        if (!isEqual(systemMessages, server.system_messages))
+            changes.system_messages = systemMessages;
 
-		client.servers.edit(server._id, changes);
-		setChanged(false);
-	}
+        client.servers.edit(server._id, changes);
+        setChanged(false);
+    }
 
-	return (
-		<div className={styles.overview}>
-			<div className={styles.row}>
-				<FileUploader
-					width={80}
-					height={80}
-					style="icon"
-					fileType="icons"
-					behaviour="upload"
-					maxFileSize={2_500_000}
-					onUpload={(icon) =>
-						client.servers.edit(server._id, { icon })
-					}
-					previewURL={client.servers.getIconURL(
-						server._id,
-						{ max_side: 256 },
-						true,
-					)}
-					remove={() =>
-						client.servers.edit(server._id, { remove: "Icon" })
-					}
-				/>
-				<div className={styles.name}>
-					<h3>
-						<Text id="app.main.servers.name" />
-					</h3>
-					<InputBox
-						contrast
-						value={name}
-						maxLength={32}
-						onChange={(e) => {
-							setName(e.currentTarget.value);
-							if (!changed) setChanged(true);
-						}}
-					/>
-				</div>
-			</div>
+    return (
+        <div className={styles.overview}>
+            <div className={styles.row}>
+                <FileUploader
+                    width={80}
+                    height={80}
+                    style="icon"
+                    fileType="icons"
+                    behaviour="upload"
+                    maxFileSize={2_500_000}
+                    onUpload={(icon) =>
+                        client.servers.edit(server._id, { icon })
+                    }
+                    previewURL={client.servers.getIconURL(
+                        server._id,
+                        { max_side: 256 },
+                        true,
+                    )}
+                    remove={() =>
+                        client.servers.edit(server._id, { remove: "Icon" })
+                    }
+                />
+                <div className={styles.name}>
+                    <h3>
+                        <Text id="app.main.servers.name" />
+                    </h3>
+                    <InputBox
+                        contrast
+                        value={name}
+                        maxLength={32}
+                        onChange={(e) => {
+                            setName(e.currentTarget.value);
+                            if (!changed) setChanged(true);
+                        }}
+                    />
+                </div>
+            </div>
 
-			<h3>
-				<Text id="app.main.servers.description" />
-			</h3>
-			<TextAreaAutoSize
-				maxRows={10}
-				minHeight={60}
-				maxLength={1024}
-				value={description}
-				placeholder={"Add a topic..."}
-				onChange={(ev) => {
-					setDescription(ev.currentTarget.value);
-					if (!changed) setChanged(true);
-				}}
-			/>
+            <h3>
+                <Text id="app.main.servers.description" />
+            </h3>
+            <TextAreaAutoSize
+                maxRows={10}
+                minHeight={60}
+                maxLength={1024}
+                value={description}
+                placeholder={"Add a topic..."}
+                onChange={(ev) => {
+                    setDescription(ev.currentTarget.value);
+                    if (!changed) setChanged(true);
+                }}
+            />
 
-			<h3>
-				<Text id="app.main.servers.custom_banner" />
-			</h3>
-			<FileUploader
-				height={160}
-				style="banner"
-				fileType="banners"
-				behaviour="upload"
-				maxFileSize={6_000_000}
-				onUpload={(banner) =>
-					client.servers.edit(server._id, { banner })
-				}
-				previewURL={client.servers.getBannerURL(
-					server._id,
-					{ width: 1000 },
-					true,
-				)}
-				remove={() =>
-					client.servers.edit(server._id, { remove: "Banner" })
-				}
-			/>
+            <h3>
+                <Text id="app.main.servers.custom_banner" />
+            </h3>
+            <FileUploader
+                height={160}
+                style="banner"
+                fileType="banners"
+                behaviour="upload"
+                maxFileSize={6_000_000}
+                onUpload={(banner) =>
+                    client.servers.edit(server._id, { banner })
+                }
+                previewURL={client.servers.getBannerURL(
+                    server._id,
+                    { width: 1000 },
+                    true,
+                )}
+                remove={() =>
+                    client.servers.edit(server._id, { remove: "Banner" })
+                }
+            />
 
-			<h3>
-				<Text id="app.settings.server_pages.overview.system_messages" />
-			</h3>
-			{[
-				["User Joined", "user_joined"],
-				["User Left", "user_left"],
-				["User Kicked", "user_kicked"],
-				["User Banned", "user_banned"],
-			].map(([i18n, key]) => (
-				// ! FIXME: temporary code just so we can expose the options
-				<p
-					style={{
-						display: "flex",
-						gap: "8px",
-						alignItems: "center",
-					}}>
-					<span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
-					<ComboBox
-						value={
-							systemMessages?.[
-								key as keyof typeof systemMessages
-							] ?? "disabled"
-						}
-						onChange={(e) => {
-							if (!changed) setChanged(true);
-							const v = e.currentTarget.value;
-							if (v === "disabled") {
-								const {
-									[key as keyof typeof systemMessages]: _,
-									...other
-								} = systemMessages;
-								setSystemMessages(other);
-							} else {
-								setSystemMessages({
-									...systemMessages,
-									[key]: v,
-								});
-							}
-						}}>
-						<option value="disabled">
-							<Text id="general.disabled" />
-						</option>
-						{server.channels.map((id) => {
-							const channel = client.channels.get(id);
-							if (!channel) return null;
-							return (
-								<option value={id}>
-									{getChannelName(client, channel, true)}
-								</option>
-							);
-						})}
-					</ComboBox>
-				</p>
-			))}
+            <h3>
+                <Text id="app.settings.server_pages.overview.system_messages" />
+            </h3>
+            {[
+                ["User Joined", "user_joined"],
+                ["User Left", "user_left"],
+                ["User Kicked", "user_kicked"],
+                ["User Banned", "user_banned"],
+            ].map(([i18n, key]) => (
+                // ! FIXME: temporary code just so we can expose the options
+                <p
+                    style={{
+                        display: "flex",
+                        gap: "8px",
+                        alignItems: "center",
+                    }}>
+                    <span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
+                    <ComboBox
+                        value={
+                            systemMessages?.[
+                                key as keyof typeof systemMessages
+                            ] ?? "disabled"
+                        }
+                        onChange={(e) => {
+                            if (!changed) setChanged(true);
+                            const v = e.currentTarget.value;
+                            if (v === "disabled") {
+                                const {
+                                    [key as keyof typeof systemMessages]: _,
+                                    ...other
+                                } = systemMessages;
+                                setSystemMessages(other);
+                            } else {
+                                setSystemMessages({
+                                    ...systemMessages,
+                                    [key]: v,
+                                });
+                            }
+                        }}>
+                        <option value="disabled">
+                            <Text id="general.disabled" />
+                        </option>
+                        {server.channels.map((id) => {
+                            const channel = client.channels.get(id);
+                            if (!channel) return null;
+                            return (
+                                <option value={id}>
+                                    {getChannelName(client, channel, true)}
+                                </option>
+                            );
+                        })}
+                    </ComboBox>
+                </p>
+            ))}
 
-			<p>
-				<Button onClick={save} contrast disabled={!changed}>
-					<Text id="app.special.modals.actions.save" />
-				</Button>
-			</p>
-		</div>
-	);
+            <p>
+                <Button onClick={save} contrast disabled={!changed}>
+                    <Text id="app.special.modals.actions.save" />
+                </Button>
+            </p>
+        </div>
+    );
 }
diff --git a/src/pages/settings/server/Roles.tsx b/src/pages/settings/server/Roles.tsx
index 78f9b81..2a84bc5 100644
--- a/src/pages/settings/server/Roles.tsx
+++ b/src/pages/settings/server/Roles.tsx
@@ -2,8 +2,8 @@ import { Plus } from "@styled-icons/boxicons-regular";
 import isEqual from "lodash.isequal";
 import { Servers } from "revolt.js/dist/api/objects";
 import {
-	ChannelPermission,
-	ServerPermission,
+    ChannelPermission,
+    ServerPermission,
 } from "revolt.js/dist/api/permissions";
 
 import styles from "./Panes.module.scss";
@@ -23,157 +23,157 @@ import Tip from "../../../components/ui/Tip";
 import ButtonItem from "../../../components/navigation/items/ButtonItem";
 
 interface Props {
-	server: Servers.Server;
+    server: Servers.Server;
 }
 
 const I32ToU32 = (arr: number[]) => arr.map((x) => x >>> 0);
 
 // ! FIXME: bad code :)
 export function Roles({ server }: Props) {
-	const [role, setRole] = useState("default");
-	const { openScreen } = useIntermediate();
-	const client = useContext(AppContext);
-	const roles = server.roles ?? {};
+    const [role, setRole] = useState("default");
+    const { openScreen } = useIntermediate();
+    const client = useContext(AppContext);
+    const roles = server.roles ?? {};
 
-	if (role !== "default" && typeof roles[role] === "undefined") {
-		useEffect(() => setRole("default"));
-		return null;
-	}
+    if (role !== "default" && typeof roles[role] === "undefined") {
+        useEffect(() => setRole("default"));
+        return null;
+    }
 
-	const v = (id: string) =>
-		I32ToU32(
-			id === "default"
-				? server.default_permissions
-				: roles[id].permissions,
-		);
-	const [perm, setPerm] = useState(v(role));
-	useEffect(() => setPerm(v(role)), [role, roles[role]?.permissions]);
+    const v = (id: string) =>
+        I32ToU32(
+            id === "default"
+                ? server.default_permissions
+                : roles[id].permissions,
+        );
+    const [perm, setPerm] = useState(v(role));
+    useEffect(() => setPerm(v(role)), [role, roles[role]?.permissions]);
 
-	const modified = !isEqual(perm, v(role));
-	const save = () =>
-		client.servers.setPermissions(server._id, role, {
-			server: perm[0],
-			channel: perm[1],
-		});
-	const deleteRole = () => {
-		setRole("default");
-		client.servers.deleteRole(server._id, role);
-	};
+    const modified = !isEqual(perm, v(role));
+    const save = () =>
+        client.servers.setPermissions(server._id, role, {
+            server: perm[0],
+            channel: perm[1],
+        });
+    const deleteRole = () => {
+        setRole("default");
+        client.servers.deleteRole(server._id, role);
+    };
 
-	return (
-		<div className={styles.roles}>
-			<div className={styles.list}>
-				<div className={styles.title}>
-					<h1>
-						<Text id="app.settings.server_pages.roles.title" />
-					</h1>
-					<Plus
-						size={22}
-						onClick={() =>
-							openScreen({
-								id: "special_input",
-								type: "create_role",
-								server: server._id,
-								callback: (id) => setRole(id),
-							})
-						}
-					/>
-				</div>
-				{["default", ...Object.keys(roles)].map((id) => {
-					if (id === "default") {
-						return (
-							<ButtonItem
-								active={role === "default"}
-								onClick={() => setRole("default")}>
-								<Text id="app.settings.permissions.default_role" />
-							</ButtonItem>
-						);
-					} else {
-						return (
-							<ButtonItem
-								active={role === id}
-								onClick={() => setRole(id)}>
-								{roles[id].name}
-							</ButtonItem>
-						);
-					}
-				})}
-			</div>
-			<div className={styles.permissions}>
-				<div className={styles.title}>
-					<h2>
-						{role === "default" ? (
-							<Text id="app.settings.permissions.default_role" />
-						) : (
-							roles[role].name
-						)}
-					</h2>
-					<Button contrast disabled={!modified} onClick={save}>
-						Save
-					</Button>
-				</div>
-				<section>
-					<Overline type="subtle">
-						<Text id="app.settings.permissions.server" />
-					</Overline>
-					{Object.keys(ServerPermission).map((key) => {
-						if (key === "View") return;
-						let value =
-							ServerPermission[
-								key as keyof typeof ServerPermission
-							];
+    return (
+        <div className={styles.roles}>
+            <div className={styles.list}>
+                <div className={styles.title}>
+                    <h1>
+                        <Text id="app.settings.server_pages.roles.title" />
+                    </h1>
+                    <Plus
+                        size={22}
+                        onClick={() =>
+                            openScreen({
+                                id: "special_input",
+                                type: "create_role",
+                                server: server._id,
+                                callback: (id) => setRole(id),
+                            })
+                        }
+                    />
+                </div>
+                {["default", ...Object.keys(roles)].map((id) => {
+                    if (id === "default") {
+                        return (
+                            <ButtonItem
+                                active={role === "default"}
+                                onClick={() => setRole("default")}>
+                                <Text id="app.settings.permissions.default_role" />
+                            </ButtonItem>
+                        );
+                    } else {
+                        return (
+                            <ButtonItem
+                                active={role === id}
+                                onClick={() => setRole(id)}>
+                                {roles[id].name}
+                            </ButtonItem>
+                        );
+                    }
+                })}
+            </div>
+            <div className={styles.permissions}>
+                <div className={styles.title}>
+                    <h2>
+                        {role === "default" ? (
+                            <Text id="app.settings.permissions.default_role" />
+                        ) : (
+                            roles[role].name
+                        )}
+                    </h2>
+                    <Button contrast disabled={!modified} onClick={save}>
+                        Save
+                    </Button>
+                </div>
+                <section>
+                    <Overline type="subtle">
+                        <Text id="app.settings.permissions.server" />
+                    </Overline>
+                    {Object.keys(ServerPermission).map((key) => {
+                        if (key === "View") return;
+                        let value =
+                            ServerPermission[
+                                key as keyof typeof ServerPermission
+                            ];
 
-						return (
-							<Checkbox
-								checked={(perm[0] & value) > 0}
-								onChange={() =>
-									setPerm([perm[0] ^ value, perm[1]])
-								}
-								description={
-									<Text id={`permissions.server.${key}.d`} />
-								}>
-								<Text id={`permissions.server.${key}.t`} />
-							</Checkbox>
-						);
-					})}
-				</section>
-				<section>
-					<Overline type="subtle">
-						<Text id="app.settings.permissions.channel" />
-					</Overline>
-					{Object.keys(ChannelPermission).map((key) => {
-						if (key === "ManageChannel") return;
-						let value =
-							ChannelPermission[
-								key as keyof typeof ChannelPermission
-							];
+                        return (
+                            <Checkbox
+                                checked={(perm[0] & value) > 0}
+                                onChange={() =>
+                                    setPerm([perm[0] ^ value, perm[1]])
+                                }
+                                description={
+                                    <Text id={`permissions.server.${key}.d`} />
+                                }>
+                                <Text id={`permissions.server.${key}.t`} />
+                            </Checkbox>
+                        );
+                    })}
+                </section>
+                <section>
+                    <Overline type="subtle">
+                        <Text id="app.settings.permissions.channel" />
+                    </Overline>
+                    {Object.keys(ChannelPermission).map((key) => {
+                        if (key === "ManageChannel") return;
+                        let value =
+                            ChannelPermission[
+                                key as keyof typeof ChannelPermission
+                            ];
 
-						return (
-							<Checkbox
-								checked={((perm[1] >>> 0) & value) > 0}
-								onChange={() =>
-									setPerm([perm[0], perm[1] ^ value])
-								}
-								disabled={key === "View"}
-								description={
-									<Text id={`permissions.channel.${key}.d`} />
-								}>
-								<Text id={`permissions.channel.${key}.t`} />
-							</Checkbox>
-						);
-					})}
-				</section>
-				<div className={styles.actions}>
-					<Button contrast disabled={!modified} onClick={save}>
-						Save
-					</Button>
-					{role !== "default" && (
-						<Button contrast error onClick={deleteRole}>
-							Delete
-						</Button>
-					)}
-				</div>
-			</div>
-		</div>
-	);
+                        return (
+                            <Checkbox
+                                checked={((perm[1] >>> 0) & value) > 0}
+                                onChange={() =>
+                                    setPerm([perm[0], perm[1] ^ value])
+                                }
+                                disabled={key === "View"}
+                                description={
+                                    <Text id={`permissions.channel.${key}.d`} />
+                                }>
+                                <Text id={`permissions.channel.${key}.t`} />
+                            </Checkbox>
+                        );
+                    })}
+                </section>
+                <div className={styles.actions}>
+                    <Button contrast disabled={!modified} onClick={save}>
+                        Save
+                    </Button>
+                    {role !== "default" && (
+                        <Button contrast error onClick={deleteRole}>
+                            Delete
+                        </Button>
+                    )}
+                </div>
+            </div>
+        </div>
+    );
 }
diff --git a/src/redux/State.tsx b/src/redux/State.tsx
index 7bc87e1..bd89e25 100644
--- a/src/redux/State.tsx
+++ b/src/redux/State.tsx
@@ -7,22 +7,22 @@ import { dispatch, State, store } from ".";
 import { Children } from "../types/Preact";
 
 interface Props {
-	children: Children;
+    children: Children;
 }
 
 export default function StateLoader(props: Props) {
-	const [loaded, setLoaded] = useState(false);
+    const [loaded, setLoaded] = useState(false);
 
-	useEffect(() => {
-		localForage.getItem("state").then((state) => {
-			if (state !== null) {
-				dispatch({ type: "__INIT", state: state as State });
-			}
+    useEffect(() => {
+        localForage.getItem("state").then((state) => {
+            if (state !== null) {
+                dispatch({ type: "__INIT", state: state as State });
+            }
 
-			setLoaded(true);
-		});
-	}, []);
+            setLoaded(true);
+        });
+    }, []);
 
-	if (!loaded) return null;
-	return <Provider store={store}>{props.children}</Provider>;
+    if (!loaded) return null;
+    return <Provider store={store}>{props.children}</Provider>;
 }
diff --git a/src/redux/connector.tsx b/src/redux/connector.tsx
index 16eef2e..f4953f0 100644
--- a/src/redux/connector.tsx
+++ b/src/redux/connector.tsx
@@ -7,10 +7,10 @@ import { memo } from "preact/compat";
 import { State } from ".";
 
 export function connectState<T>(
-	component: (props: any) => h.JSX.Element | null,
-	mapKeys: (state: State, props: T) => any,
-	memoize?: boolean,
+    component: (props: any) => h.JSX.Element | null,
+    mapKeys: (state: State, props: T) => any,
+    memoize?: boolean,
 ): ConnectedComponent<(props: any) => h.JSX.Element | null, T> {
-	let c = connect(mapKeys)(component);
-	return memoize ? memo(c) : c;
+    let c = connect(mapKeys)(component);
+    return memoize ? memo(c) : c;
 }
diff --git a/src/redux/index.ts b/src/redux/index.ts
index b1969de..24db71b 100644
--- a/src/redux/index.ts
+++ b/src/redux/index.ts
@@ -18,67 +18,67 @@ import { Typing } from "./reducers/typing";
 import { Unreads } from "./reducers/unreads";
 
 export type State = {
-	config: Core.RevoltNodeConfiguration;
-	locale: Language;
-	auth: AuthState;
-	settings: Settings;
-	unreads: Unreads;
-	queue: QueuedMessage[];
-	typing: Typing;
-	drafts: Drafts;
-	sync: SyncOptions;
-	experiments: ExperimentOptions;
-	lastOpened: LastOpened;
-	notifications: Notifications;
-	sectionToggle: SectionToggle;
+    config: Core.RevoltNodeConfiguration;
+    locale: Language;
+    auth: AuthState;
+    settings: Settings;
+    unreads: Unreads;
+    queue: QueuedMessage[];
+    typing: Typing;
+    drafts: Drafts;
+    sync: SyncOptions;
+    experiments: ExperimentOptions;
+    lastOpened: LastOpened;
+    notifications: Notifications;
+    sectionToggle: SectionToggle;
 };
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export const store = createStore((state: any, action: any) => {
-	if (import.meta.env.DEV) {
-		console.debug("State Update:", action);
-	}
+    if (import.meta.env.DEV) {
+        console.debug("State Update:", action);
+    }
 
-	if (action.type === "__INIT") {
-		return action.state;
-	}
+    if (action.type === "__INIT") {
+        return action.state;
+    }
 
-	return rootReducer(state, action);
+    return rootReducer(state, action);
 });
 
 // Save state using localForage.
 store.subscribe(() => {
-	const {
-		config,
-		locale,
-		auth,
-		settings,
-		unreads,
-		queue,
-		drafts,
-		sync,
-		experiments,
-		lastOpened,
-		notifications,
-		sectionToggle,
-	} = store.getState() as State;
+    const {
+        config,
+        locale,
+        auth,
+        settings,
+        unreads,
+        queue,
+        drafts,
+        sync,
+        experiments,
+        lastOpened,
+        notifications,
+        sectionToggle,
+    } = store.getState() as State;
 
-	localForage.setItem("state", {
-		config,
-		locale,
-		auth,
-		settings,
-		unreads,
-		queue,
-		drafts,
-		sync,
-		experiments,
-		lastOpened,
-		notifications,
-		sectionToggle,
-	});
+    localForage.setItem("state", {
+        config,
+        locale,
+        auth,
+        settings,
+        unreads,
+        queue,
+        drafts,
+        sync,
+        experiments,
+        lastOpened,
+        notifications,
+        sectionToggle,
+    });
 });
 
 export function dispatch(action: Action) {
-	store.dispatch(action);
+    store.dispatch(action);
 }
diff --git a/src/redux/reducers/auth.ts b/src/redux/reducers/auth.ts
index a91a88e..f253334 100644
--- a/src/redux/reducers/auth.ts
+++ b/src/redux/reducers/auth.ts
@@ -1,49 +1,49 @@
 import type { Auth } from "revolt.js/dist/api/objects";
 
 export interface AuthState {
-	accounts: {
-		[key: string]: {
-			session: Auth.Session;
-		};
-	};
-	active?: string;
+    accounts: {
+        [key: string]: {
+            session: Auth.Session;
+        };
+    };
+    active?: string;
 }
 
 export type AuthAction =
-	| { type: undefined }
-	| {
-			type: "LOGIN";
-			session: Auth.Session;
-	  }
-	| {
-			type: "LOGOUT";
-			user_id?: string;
-	  };
+    | { type: undefined }
+    | {
+          type: "LOGIN";
+          session: Auth.Session;
+      }
+    | {
+          type: "LOGOUT";
+          user_id?: string;
+      };
 
 export function auth(
-	state = { accounts: {} } as AuthState,
-	action: AuthAction,
+    state = { accounts: {} } as AuthState,
+    action: AuthAction,
 ): AuthState {
-	switch (action.type) {
-		case "LOGIN":
-			return {
-				accounts: {
-					...state.accounts,
-					[action.session.user_id]: {
-						session: action.session,
-					},
-				},
-				active: action.session.user_id,
-			};
-		case "LOGOUT": {
-			const accounts = Object.assign({}, state.accounts);
-			action.user_id && delete accounts[action.user_id];
+    switch (action.type) {
+        case "LOGIN":
+            return {
+                accounts: {
+                    ...state.accounts,
+                    [action.session.user_id]: {
+                        session: action.session,
+                    },
+                },
+                active: action.session.user_id,
+            };
+        case "LOGOUT": {
+            const accounts = Object.assign({}, state.accounts);
+            action.user_id && delete accounts[action.user_id];
 
-			return {
-				accounts,
-			};
-		}
-		default:
-			return state;
-	}
+            return {
+                accounts,
+            };
+        }
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/drafts.ts b/src/redux/reducers/drafts.ts
index d34c29b..4f36a84 100644
--- a/src/redux/reducers/drafts.ts
+++ b/src/redux/reducers/drafts.ts
@@ -1,35 +1,35 @@
 export type Drafts = { [key: string]: string };
 
 export type DraftAction =
-	| { type: undefined }
-	| {
-			type: "SET_DRAFT";
-			channel: string;
-			content: string;
-	  }
-	| {
-			type: "CLEAR_DRAFT";
-			channel: string;
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "SET_DRAFT";
+          channel: string;
+          content: string;
+      }
+    | {
+          type: "CLEAR_DRAFT";
+          channel: string;
+      }
+    | {
+          type: "RESET";
+      };
 
 export function drafts(state: Drafts = {}, action: DraftAction): Drafts {
-	switch (action.type) {
-		case "SET_DRAFT":
-			return {
-				...state,
-				[action.channel]: action.content,
-			};
-		case "CLEAR_DRAFT": {
-			// eslint-disable-next-line @typescript-eslint/no-unused-vars
-			const { [action.channel]: _, ...newState } = state;
-			return newState;
-		}
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "SET_DRAFT":
+            return {
+                ...state,
+                [action.channel]: action.content,
+            };
+        case "CLEAR_DRAFT": {
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            const { [action.channel]: _, ...newState } = state;
+            return newState;
+        }
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/experiments.ts b/src/redux/reducers/experiments.ts
index 1e3b908..66d095b 100644
--- a/src/redux/reducers/experiments.ts
+++ b/src/redux/reducers/experiments.ts
@@ -2,43 +2,43 @@ export type Experiments = never;
 export const AVAILABLE_EXPERIMENTS: Experiments[] = [];
 
 export interface ExperimentOptions {
-	enabled?: Experiments[];
+    enabled?: Experiments[];
 }
 
 export type ExperimentsAction =
-	| { type: undefined }
-	| {
-			type: "EXPERIMENTS_ENABLE";
-			key: Experiments;
-	  }
-	| {
-			type: "EXPERIMENTS_DISABLE";
-			key: Experiments;
-	  };
+    | { type: undefined }
+    | {
+          type: "EXPERIMENTS_ENABLE";
+          key: Experiments;
+      }
+    | {
+          type: "EXPERIMENTS_DISABLE";
+          key: Experiments;
+      };
 
 export function experiments(
-	state = {} as ExperimentOptions,
-	action: ExperimentsAction,
+    state = {} as ExperimentOptions,
+    action: ExperimentsAction,
 ): ExperimentOptions {
-	switch (action.type) {
-		case "EXPERIMENTS_ENABLE":
-			return {
-				...state,
-				enabled: [
-					...(state.enabled ?? [])
-						.filter((x) => AVAILABLE_EXPERIMENTS.includes(x))
-						.filter((v) => v !== action.key),
-					action.key,
-				],
-			};
-		case "EXPERIMENTS_DISABLE":
-			return {
-				...state,
-				enabled: state.enabled
-					?.filter((v) => v !== action.key)
-					.filter((x) => AVAILABLE_EXPERIMENTS.includes(x)),
-			};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "EXPERIMENTS_ENABLE":
+            return {
+                ...state,
+                enabled: [
+                    ...(state.enabled ?? [])
+                        .filter((x) => AVAILABLE_EXPERIMENTS.includes(x))
+                        .filter((v) => v !== action.key),
+                    action.key,
+                ],
+            };
+        case "EXPERIMENTS_DISABLE":
+            return {
+                ...state,
+                enabled: state.enabled
+                    ?.filter((v) => v !== action.key)
+                    .filter((x) => AVAILABLE_EXPERIMENTS.includes(x)),
+            };
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/index.ts b/src/redux/reducers/index.ts
index 83e3fb3..029d5f5 100644
--- a/src/redux/reducers/index.ts
+++ b/src/redux/reducers/index.ts
@@ -16,45 +16,45 @@ import { typing, TypingAction } from "./typing";
 import { unreads, UnreadsAction } from "./unreads";
 
 export default combineReducers({
-	config,
-	locale,
-	auth,
-	settings,
-	unreads,
-	queue,
-	typing,
-	drafts,
-	sync,
-	experiments,
-	lastOpened,
-	notifications,
-	sectionToggle,
+    config,
+    locale,
+    auth,
+    settings,
+    unreads,
+    queue,
+    typing,
+    drafts,
+    sync,
+    experiments,
+    lastOpened,
+    notifications,
+    sectionToggle,
 });
 
 export type Action =
-	| ConfigAction
-	| LocaleAction
-	| AuthAction
-	| SettingsAction
-	| UnreadsAction
-	| QueueAction
-	| TypingAction
-	| DraftAction
-	| SyncAction
-	| ExperimentsAction
-	| LastOpenedAction
-	| NotificationsAction
-	| SectionToggleAction
-	| { type: "__INIT"; state: State };
+    | ConfigAction
+    | LocaleAction
+    | AuthAction
+    | SettingsAction
+    | UnreadsAction
+    | QueueAction
+    | TypingAction
+    | DraftAction
+    | SyncAction
+    | ExperimentsAction
+    | LastOpenedAction
+    | NotificationsAction
+    | SectionToggleAction
+    | { type: "__INIT"; state: State };
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export function filter(obj: any, keys: string[]) {
-	// eslint-disable-next-line @typescript-eslint/no-explicit-any
-	const newObj: any = {};
-	for (const key of keys) {
-		const v = obj[key];
-		if (v) newObj[key] = v;
-	}
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    const newObj: any = {};
+    for (const key of keys) {
+        const v = obj[key];
+        if (v) newObj[key] = v;
+    }
 
-	return newObj;
+    return newObj;
 }
diff --git a/src/redux/reducers/last_opened.ts b/src/redux/reducers/last_opened.ts
index b4c89d1..e49c444 100644
--- a/src/redux/reducers/last_opened.ts
+++ b/src/redux/reducers/last_opened.ts
@@ -1,32 +1,32 @@
 export interface LastOpened {
-	[key: string]: string;
+    [key: string]: string;
 }
 
 export type LastOpenedAction =
-	| { type: undefined }
-	| {
-			type: "LAST_OPENED_SET";
-			parent: string;
-			child: string;
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "LAST_OPENED_SET";
+          parent: string;
+          child: string;
+      }
+    | {
+          type: "RESET";
+      };
 
 export function lastOpened(
-	state = {} as LastOpened,
-	action: LastOpenedAction,
+    state = {} as LastOpened,
+    action: LastOpenedAction,
 ): LastOpened {
-	switch (action.type) {
-		case "LAST_OPENED_SET": {
-			return {
-				...state,
-				[action.parent]: action.child,
-			};
-		}
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "LAST_OPENED_SET": {
+            return {
+                ...state,
+                [action.parent]: action.child,
+            };
+        }
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/locale.ts b/src/redux/reducers/locale.ts
index fd695a1..20c90ab 100644
--- a/src/redux/reducers/locale.ts
+++ b/src/redux/reducers/locale.ts
@@ -3,50 +3,50 @@ import { Language } from "../../context/Locale";
 import type { SyncUpdateAction } from "./sync";
 
 export type LocaleAction =
-	| { type: undefined }
-	| {
-			type: "SET_LOCALE";
-			locale: Language;
-	  }
-	| SyncUpdateAction;
+    | { type: undefined }
+    | {
+          type: "SET_LOCALE";
+          locale: Language;
+      }
+    | SyncUpdateAction;
 
 export function findLanguage(lang?: string): Language {
-	if (!lang) {
-		if (typeof navigator === "undefined") {
-			lang = Language.ENGLISH;
-		} else {
-			lang = navigator.language;
-		}
-	}
-
-	const code = lang.replace("-", "_");
-	const short = code.split("_")[0];
-
-	const values = [];
-	for (const key in Language) {
-		const value = Language[key as keyof typeof Language];
-		values.push(value);
-		if (value.startsWith(code)) {
-			return value as Language;
-		}
-	}
-
-	for (const value of values.reverse()) {
-		if (value.startsWith(short)) {
-			return value as Language;
-		}
-	}
-
-	return Language.ENGLISH;
+    if (!lang) {
+        if (typeof navigator === "undefined") {
+            lang = Language.ENGLISH;
+        } else {
+            lang = navigator.language;
+        }
+    }
+
+    const code = lang.replace("-", "_");
+    const short = code.split("_")[0];
+
+    const values = [];
+    for (const key in Language) {
+        const value = Language[key as keyof typeof Language];
+        values.push(value);
+        if (value.startsWith(code)) {
+            return value as Language;
+        }
+    }
+
+    for (const value of values.reverse()) {
+        if (value.startsWith(short)) {
+            return value as Language;
+        }
+    }
+
+    return Language.ENGLISH;
 }
 
 export function locale(state = findLanguage(), action: LocaleAction): Language {
-	switch (action.type) {
-		case "SET_LOCALE":
-			return action.locale;
-		case "SYNC_UPDATE":
-			return (action.update.locale?.[1] ?? state) as Language;
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "SET_LOCALE":
+            return action.locale;
+        case "SYNC_UPDATE":
+            return (action.update.locale?.[1] ?? state) as Language;
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/notifications.ts b/src/redux/reducers/notifications.ts
index cb0f34d..ace9d56 100644
--- a/src/redux/reducers/notifications.ts
+++ b/src/redux/reducers/notifications.ts
@@ -5,78 +5,78 @@ import type { SyncUpdateAction } from "./sync";
 export type NotificationState = "all" | "mention" | "none" | "muted";
 
 export type Notifications = {
-	[key: string]: NotificationState;
+    [key: string]: NotificationState;
 };
 
 export const DEFAULT_STATES: {
-	[key in Channel["channel_type"]]: NotificationState;
+    [key in Channel["channel_type"]]: NotificationState;
 } = {
-	SavedMessages: "all",
-	DirectMessage: "all",
-	Group: "all",
-	TextChannel: "mention",
-	VoiceChannel: "mention",
+    SavedMessages: "all",
+    DirectMessage: "all",
+    Group: "all",
+    TextChannel: "mention",
+    VoiceChannel: "mention",
 };
 
 export function getNotificationState(
-	notifications: Notifications,
-	channel: Channel,
+    notifications: Notifications,
+    channel: Channel,
 ) {
-	return notifications[channel._id] ?? DEFAULT_STATES[channel.channel_type];
+    return notifications[channel._id] ?? DEFAULT_STATES[channel.channel_type];
 }
 
 export function shouldNotify(
-	state: NotificationState,
-	message: Message,
-	user_id: string,
+    state: NotificationState,
+    message: Message,
+    user_id: string,
 ) {
-	switch (state) {
-		case "muted":
-		case "none":
-			return false;
-		case "mention": {
-			if (!message.mentions?.includes(user_id)) return false;
-		}
-	}
+    switch (state) {
+        case "muted":
+        case "none":
+            return false;
+        case "mention": {
+            if (!message.mentions?.includes(user_id)) return false;
+        }
+    }
 
-	return true;
+    return true;
 }
 
 export type NotificationsAction =
-	| { type: undefined }
-	| {
-			type: "NOTIFICATIONS_SET";
-			key: string;
-			state: NotificationState;
-	  }
-	| {
-			type: "NOTIFICATIONS_REMOVE";
-			key: string;
-	  }
-	| SyncUpdateAction
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "NOTIFICATIONS_SET";
+          key: string;
+          state: NotificationState;
+      }
+    | {
+          type: "NOTIFICATIONS_REMOVE";
+          key: string;
+      }
+    | SyncUpdateAction
+    | {
+          type: "RESET";
+      };
 
 export function notifications(
-	state = {} as Notifications,
-	action: NotificationsAction,
+    state = {} as Notifications,
+    action: NotificationsAction,
 ): Notifications {
-	switch (action.type) {
-		case "NOTIFICATIONS_SET":
-			return {
-				...state,
-				[action.key]: action.state,
-			};
-		case "NOTIFICATIONS_REMOVE": {
-			const { [action.key]: _, ...newState } = state;
-			return newState;
-		}
-		case "SYNC_UPDATE":
-			return action.update.notifications?.[1] ?? state;
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "NOTIFICATIONS_SET":
+            return {
+                ...state,
+                [action.key]: action.state,
+            };
+        case "NOTIFICATIONS_REMOVE": {
+            const { [action.key]: _, ...newState } = state;
+            return newState;
+        }
+        case "SYNC_UPDATE":
+            return action.update.notifications?.[1] ?? state;
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/queue.ts b/src/redux/reducers/queue.ts
index 20020e0..ce8ee12 100644
--- a/src/redux/reducers/queue.ts
+++ b/src/redux/reducers/queue.ts
@@ -1,113 +1,113 @@
 import type { MessageObject } from "../../context/revoltjs/util";
 
 export enum QueueStatus {
-	SENDING = "sending",
-	ERRORED = "errored",
+    SENDING = "sending",
+    ERRORED = "errored",
 }
 
 export interface Reply {
-	id: string;
-	mention: boolean;
+    id: string;
+    mention: boolean;
 }
 
 export type QueuedMessageData = Omit<MessageObject, "content" | "replies"> & {
-	content: string;
-	replies: Reply[];
+    content: string;
+    replies: Reply[];
 };
 
 export interface QueuedMessage {
-	id: string;
-	channel: string;
-	data: QueuedMessageData;
-	status: QueueStatus;
-	error?: string;
+    id: string;
+    channel: string;
+    data: QueuedMessageData;
+    status: QueueStatus;
+    error?: string;
 }
 
 export type QueueAction =
-	| { type: undefined }
-	| {
-			type: "QUEUE_ADD";
-			nonce: string;
-			channel: string;
-			message: QueuedMessageData;
-	  }
-	| {
-			type: "QUEUE_FAIL";
-			nonce: string;
-			error: string;
-	  }
-	| {
-			type: "QUEUE_START";
-			nonce: string;
-	  }
-	| {
-			type: "QUEUE_REMOVE";
-			nonce: string;
-	  }
-	| {
-			type: "QUEUE_DROP_ALL";
-	  }
-	| {
-			type: "QUEUE_FAIL_ALL";
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "QUEUE_ADD";
+          nonce: string;
+          channel: string;
+          message: QueuedMessageData;
+      }
+    | {
+          type: "QUEUE_FAIL";
+          nonce: string;
+          error: string;
+      }
+    | {
+          type: "QUEUE_START";
+          nonce: string;
+      }
+    | {
+          type: "QUEUE_REMOVE";
+          nonce: string;
+      }
+    | {
+          type: "QUEUE_DROP_ALL";
+      }
+    | {
+          type: "QUEUE_FAIL_ALL";
+      }
+    | {
+          type: "RESET";
+      };
 
 export function queue(
-	state: QueuedMessage[] = [],
-	action: QueueAction,
+    state: QueuedMessage[] = [],
+    action: QueueAction,
 ): QueuedMessage[] {
-	switch (action.type) {
-		case "QUEUE_ADD": {
-			return [
-				...state.filter((x) => x.id !== action.nonce),
-				{
-					id: action.nonce,
-					data: action.message,
-					channel: action.channel,
-					status: QueueStatus.SENDING,
-				},
-			];
-		}
-		case "QUEUE_FAIL": {
-			const entry = state.find(
-				(x) => x.id === action.nonce,
-			) as QueuedMessage;
-			return [
-				...state.filter((x) => x.id !== action.nonce),
-				{
-					...entry,
-					status: QueueStatus.ERRORED,
-					error: action.error,
-				},
-			];
-		}
-		case "QUEUE_START": {
-			const entry = state.find(
-				(x) => x.id === action.nonce,
-			) as QueuedMessage;
-			return [
-				...state.filter((x) => x.id !== action.nonce),
-				{
-					...entry,
-					status: QueueStatus.SENDING,
-				},
-			];
-		}
-		case "QUEUE_REMOVE":
-			return state.filter((x) => x.id !== action.nonce);
-		case "QUEUE_FAIL_ALL":
-			return state.map((x) => {
-				return {
-					...x,
-					status: QueueStatus.ERRORED,
-				};
-			});
-		case "QUEUE_DROP_ALL":
-		case "RESET":
-			return [];
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "QUEUE_ADD": {
+            return [
+                ...state.filter((x) => x.id !== action.nonce),
+                {
+                    id: action.nonce,
+                    data: action.message,
+                    channel: action.channel,
+                    status: QueueStatus.SENDING,
+                },
+            ];
+        }
+        case "QUEUE_FAIL": {
+            const entry = state.find(
+                (x) => x.id === action.nonce,
+            ) as QueuedMessage;
+            return [
+                ...state.filter((x) => x.id !== action.nonce),
+                {
+                    ...entry,
+                    status: QueueStatus.ERRORED,
+                    error: action.error,
+                },
+            ];
+        }
+        case "QUEUE_START": {
+            const entry = state.find(
+                (x) => x.id === action.nonce,
+            ) as QueuedMessage;
+            return [
+                ...state.filter((x) => x.id !== action.nonce),
+                {
+                    ...entry,
+                    status: QueueStatus.SENDING,
+                },
+            ];
+        }
+        case "QUEUE_REMOVE":
+            return state.filter((x) => x.id !== action.nonce);
+        case "QUEUE_FAIL_ALL":
+            return state.map((x) => {
+                return {
+                    ...x,
+                    status: QueueStatus.ERRORED,
+                };
+            });
+        case "QUEUE_DROP_ALL":
+        case "RESET":
+            return [];
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/section_toggle.ts b/src/redux/reducers/section_toggle.ts
index 68657fb..cff440d 100644
--- a/src/redux/reducers/section_toggle.ts
+++ b/src/redux/reducers/section_toggle.ts
@@ -1,40 +1,40 @@
 export interface SectionToggle {
-	[key: string]: boolean;
+    [key: string]: boolean;
 }
 
 export type SectionToggleAction =
-	| { type: undefined }
-	| {
-			type: "SECTION_TOGGLE_SET";
-			id: string;
-			state: boolean;
-	  }
-	| {
-			type: "SECTION_TOGGLE_UNSET";
-			id: string;
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "SECTION_TOGGLE_SET";
+          id: string;
+          state: boolean;
+      }
+    | {
+          type: "SECTION_TOGGLE_UNSET";
+          id: string;
+      }
+    | {
+          type: "RESET";
+      };
 
 export function sectionToggle(
-	state = {} as SectionToggle,
-	action: SectionToggleAction,
+    state = {} as SectionToggle,
+    action: SectionToggleAction,
 ): SectionToggle {
-	switch (action.type) {
-		case "SECTION_TOGGLE_SET": {
-			return {
-				...state,
-				[action.id]: action.state,
-			};
-		}
-		case "SECTION_TOGGLE_UNSET": {
-			const { [action.id]: _, ...newState } = state;
-			return newState;
-		}
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "SECTION_TOGGLE_SET": {
+            return {
+                ...state,
+                [action.id]: action.state,
+            };
+        }
+        case "SECTION_TOGGLE_UNSET": {
+            const { [action.id]: _, ...newState } = state;
+            return newState;
+        }
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/server_config.ts b/src/redux/reducers/server_config.ts
index a33ef16..1b4da1d 100644
--- a/src/redux/reducers/server_config.ts
+++ b/src/redux/reducers/server_config.ts
@@ -1,20 +1,20 @@
 import type { Core } from "revolt.js/dist/api/objects";
 
 export type ConfigAction =
-	| { type: undefined }
-	| {
-			type: "SET_CONFIG";
-			config: Core.RevoltNodeConfiguration;
-	  };
+    | { type: undefined }
+    | {
+          type: "SET_CONFIG";
+          config: Core.RevoltNodeConfiguration;
+      };
 
 export function config(
-	state = {} as Core.RevoltNodeConfiguration,
-	action: ConfigAction,
+    state = {} as Core.RevoltNodeConfiguration,
+    action: ConfigAction,
 ): Core.RevoltNodeConfiguration {
-	switch (action.type) {
-		case "SET_CONFIG":
-			return action.config;
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "SET_CONFIG":
+            return action.config;
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts
index cf7dd12..d5c898f 100644
--- a/src/redux/reducers/settings.ts
+++ b/src/redux/reducers/settings.ts
@@ -7,106 +7,106 @@ import type { Sounds } from "../../assets/sounds/Audio";
 import type { SyncUpdateAction } from "./sync";
 
 export type SoundOptions = {
-	[key in Sounds]?: boolean;
+    [key in Sounds]?: boolean;
 };
 
 export const DEFAULT_SOUNDS: SoundOptions = {
-	message: true,
-	outbound: false,
-	call_join: true,
-	call_leave: true,
+    message: true,
+    outbound: false,
+    call_join: true,
+    call_leave: true,
 };
 
 export interface NotificationOptions {
-	desktopEnabled?: boolean;
-	sounds?: SoundOptions;
+    desktopEnabled?: boolean;
+    sounds?: SoundOptions;
 }
 
 export type EmojiPacks = "mutant" | "twemoji" | "noto" | "openmoji";
 export interface AppearanceOptions {
-	emojiPack?: EmojiPacks;
+    emojiPack?: EmojiPacks;
 }
 
 export interface Settings {
-	theme?: ThemeOptions;
-	appearance?: AppearanceOptions;
-	notification?: NotificationOptions;
+    theme?: ThemeOptions;
+    appearance?: AppearanceOptions;
+    notification?: NotificationOptions;
 }
 
 export type SettingsAction =
-	| { type: undefined }
-	| {
-			type: "SETTINGS_SET_THEME";
-			theme: ThemeOptions;
-	  }
-	| {
-			type: "SETTINGS_SET_THEME_OVERRIDE";
-			custom?: Partial<Theme>;
-	  }
-	| {
-			type: "SETTINGS_SET_NOTIFICATION_OPTIONS";
-			options: NotificationOptions;
-	  }
-	| {
-			type: "SETTINGS_SET_APPEARANCE";
-			options: Partial<AppearanceOptions>;
-	  }
-	| SyncUpdateAction
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "SETTINGS_SET_THEME";
+          theme: ThemeOptions;
+      }
+    | {
+          type: "SETTINGS_SET_THEME_OVERRIDE";
+          custom?: Partial<Theme>;
+      }
+    | {
+          type: "SETTINGS_SET_NOTIFICATION_OPTIONS";
+          options: NotificationOptions;
+      }
+    | {
+          type: "SETTINGS_SET_APPEARANCE";
+          options: Partial<AppearanceOptions>;
+      }
+    | SyncUpdateAction
+    | {
+          type: "RESET";
+      };
 
 export function settings(
-	state = {} as Settings,
-	action: SettingsAction,
+    state = {} as Settings,
+    action: SettingsAction,
 ): Settings {
-	setEmojiPack(state.appearance?.emojiPack ?? "mutant");
+    setEmojiPack(state.appearance?.emojiPack ?? "mutant");
 
-	switch (action.type) {
-		case "SETTINGS_SET_THEME":
-			return {
-				...state,
-				theme: {
-					...filter(state.theme, ["custom", "preset", "ligatures"]),
-					...action.theme,
-				},
-			};
-		case "SETTINGS_SET_THEME_OVERRIDE":
-			return {
-				...state,
-				theme: {
-					...state.theme,
-					custom: {
-						...state.theme?.custom,
-						...action.custom,
-					},
-				},
-			};
-		case "SETTINGS_SET_NOTIFICATION_OPTIONS":
-			return {
-				...state,
-				notification: {
-					...state.notification,
-					...action.options,
-				},
-			};
-		case "SETTINGS_SET_APPEARANCE":
-			return {
-				...state,
-				appearance: {
-					...filter(state.appearance, ["emojiPack"]),
-					...action.options,
-				},
-			};
-		case "SYNC_UPDATE":
-			return {
-				...state,
-				appearance: action.update.appearance?.[1] ?? state.appearance,
-				theme: action.update.theme?.[1] ?? state.theme,
-			};
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "SETTINGS_SET_THEME":
+            return {
+                ...state,
+                theme: {
+                    ...filter(state.theme, ["custom", "preset", "ligatures"]),
+                    ...action.theme,
+                },
+            };
+        case "SETTINGS_SET_THEME_OVERRIDE":
+            return {
+                ...state,
+                theme: {
+                    ...state.theme,
+                    custom: {
+                        ...state.theme?.custom,
+                        ...action.custom,
+                    },
+                },
+            };
+        case "SETTINGS_SET_NOTIFICATION_OPTIONS":
+            return {
+                ...state,
+                notification: {
+                    ...state.notification,
+                    ...action.options,
+                },
+            };
+        case "SETTINGS_SET_APPEARANCE":
+            return {
+                ...state,
+                appearance: {
+                    ...filter(state.appearance, ["emojiPack"]),
+                    ...action.options,
+                },
+            };
+        case "SYNC_UPDATE":
+            return {
+                ...state,
+                appearance: action.update.appearance?.[1] ?? state.appearance,
+                theme: action.update.theme?.[1] ?? state.theme,
+            };
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/sync.ts b/src/redux/reducers/sync.ts
index 5e465ee..e62c31c 100644
--- a/src/redux/reducers/sync.ts
+++ b/src/redux/reducers/sync.ts
@@ -7,88 +7,88 @@ import type { AppearanceOptions } from "./settings";
 export type SyncKeys = "theme" | "appearance" | "locale" | "notifications";
 
 export interface SyncData {
-	locale?: Language;
-	theme?: ThemeOptions;
-	appearance?: AppearanceOptions;
-	notifications?: Notifications;
+    locale?: Language;
+    theme?: ThemeOptions;
+    appearance?: AppearanceOptions;
+    notifications?: Notifications;
 }
 
 export const DEFAULT_ENABLED_SYNC: SyncKeys[] = [
-	"theme",
-	"appearance",
-	"locale",
-	"notifications",
+    "theme",
+    "appearance",
+    "locale",
+    "notifications",
 ];
 export interface SyncOptions {
-	disabled?: SyncKeys[];
-	revision?: {
-		[key: string]: number;
-	};
+    disabled?: SyncKeys[];
+    revision?: {
+        [key: string]: number;
+    };
 }
 
 export type SyncUpdateAction = {
-	type: "SYNC_UPDATE";
-	update: { [key in SyncKeys]?: [number, SyncData[key]] };
+    type: "SYNC_UPDATE";
+    update: { [key in SyncKeys]?: [number, SyncData[key]] };
 };
 
 export type SyncAction =
-	| { type: undefined }
-	| {
-			type: "SYNC_ENABLE_KEY";
-			key: SyncKeys;
-	  }
-	| {
-			type: "SYNC_DISABLE_KEY";
-			key: SyncKeys;
-	  }
-	| {
-			type: "SYNC_SET_REVISION";
-			key: SyncKeys;
-			timestamp: number;
-	  }
-	| SyncUpdateAction;
+    | { type: undefined }
+    | {
+          type: "SYNC_ENABLE_KEY";
+          key: SyncKeys;
+      }
+    | {
+          type: "SYNC_DISABLE_KEY";
+          key: SyncKeys;
+      }
+    | {
+          type: "SYNC_SET_REVISION";
+          key: SyncKeys;
+          timestamp: number;
+      }
+    | SyncUpdateAction;
 
 export function sync(
-	state = {} as SyncOptions,
-	action: SyncAction,
+    state = {} as SyncOptions,
+    action: SyncAction,
 ): SyncOptions {
-	switch (action.type) {
-		case "SYNC_DISABLE_KEY":
-			return {
-				...state,
-				disabled: [
-					...(state.disabled ?? []).filter((v) => v !== action.key),
-					action.key,
-				],
-			};
-		case "SYNC_ENABLE_KEY":
-			return {
-				...state,
-				disabled: state.disabled?.filter((v) => v !== action.key),
-			};
-		case "SYNC_SET_REVISION":
-			return {
-				...state,
-				revision: {
-					...state.revision,
-					[action.key]: action.timestamp,
-				},
-			};
-		case "SYNC_UPDATE": {
-			const revision = { ...state.revision };
-			for (const key of Object.keys(action.update)) {
-				const value = action.update[key as SyncKeys];
-				if (value) {
-					revision[key] = value[0];
-				}
-			}
+    switch (action.type) {
+        case "SYNC_DISABLE_KEY":
+            return {
+                ...state,
+                disabled: [
+                    ...(state.disabled ?? []).filter((v) => v !== action.key),
+                    action.key,
+                ],
+            };
+        case "SYNC_ENABLE_KEY":
+            return {
+                ...state,
+                disabled: state.disabled?.filter((v) => v !== action.key),
+            };
+        case "SYNC_SET_REVISION":
+            return {
+                ...state,
+                revision: {
+                    ...state.revision,
+                    [action.key]: action.timestamp,
+                },
+            };
+        case "SYNC_UPDATE": {
+            const revision = { ...state.revision };
+            for (const key of Object.keys(action.update)) {
+                const value = action.update[key as SyncKeys];
+                if (value) {
+                    revision[key] = value[0];
+                }
+            }
 
-			return {
-				...state,
-				revision,
-			};
-		}
-		default:
-			return state;
-	}
+            return {
+                ...state,
+                revision,
+            };
+        }
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/typing.ts b/src/redux/reducers/typing.ts
index 9dcbc59..41a08e2 100644
--- a/src/redux/reducers/typing.ts
+++ b/src/redux/reducers/typing.ts
@@ -2,47 +2,47 @@ export type TypingUser = { id: string; started: number };
 export type Typing = { [key: string]: TypingUser[] };
 
 export type TypingAction =
-	| { type: undefined }
-	| {
-			type: "TYPING_START";
-			channel: string;
-			user: string;
-	  }
-	| {
-			type: "TYPING_STOP";
-			channel: string;
-			user: string;
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "TYPING_START";
+          channel: string;
+          user: string;
+      }
+    | {
+          type: "TYPING_STOP";
+          channel: string;
+          user: string;
+      }
+    | {
+          type: "RESET";
+      };
 
 export function typing(state: Typing = {}, action: TypingAction): Typing {
-	switch (action.type) {
-		case "TYPING_START":
-			return {
-				...state,
-				[action.channel]: [
-					...(state[action.channel] ?? []).filter(
-						(x) => x.id !== action.user,
-					),
-					{
-						id: action.user,
-						started: +new Date(),
-					},
-				],
-			};
-		case "TYPING_STOP":
-			return {
-				...state,
-				[action.channel]:
-					state[action.channel]?.filter(
-						(x) => x.id !== action.user,
-					) ?? [],
-			};
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+    switch (action.type) {
+        case "TYPING_START":
+            return {
+                ...state,
+                [action.channel]: [
+                    ...(state[action.channel] ?? []).filter(
+                        (x) => x.id !== action.user,
+                    ),
+                    {
+                        id: action.user,
+                        started: +new Date(),
+                    },
+                ],
+            };
+        case "TYPING_STOP":
+            return {
+                ...state,
+                [action.channel]:
+                    state[action.channel]?.filter(
+                        (x) => x.id !== action.user,
+                    ) ?? [],
+            };
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/redux/reducers/unreads.ts b/src/redux/reducers/unreads.ts
index e9e1a9a..1f1b2fb 100644
--- a/src/redux/reducers/unreads.ts
+++ b/src/redux/reducers/unreads.ts
@@ -1,61 +1,61 @@
 import type { Sync } from "revolt.js/dist/api/objects";
 
 export interface Unreads {
-	[key: string]: Partial<Omit<Sync.ChannelUnread, "_id">>;
+    [key: string]: Partial<Omit<Sync.ChannelUnread, "_id">>;
 }
 
 export type UnreadsAction =
-	| { type: undefined }
-	| {
-			type: "UNREADS_MARK_READ";
-			channel: string;
-			message: string;
-	  }
-	| {
-			type: "UNREADS_SET";
-			unreads: Sync.ChannelUnread[];
-	  }
-	| {
-			type: "UNREADS_MENTION";
-			channel: string;
-			message: string;
-	  }
-	| {
-			type: "RESET";
-	  };
+    | { type: undefined }
+    | {
+          type: "UNREADS_MARK_READ";
+          channel: string;
+          message: string;
+      }
+    | {
+          type: "UNREADS_SET";
+          unreads: Sync.ChannelUnread[];
+      }
+    | {
+          type: "UNREADS_MENTION";
+          channel: string;
+          message: string;
+      }
+    | {
+          type: "RESET";
+      };
 
 export function unreads(state = {} as Unreads, action: UnreadsAction): Unreads {
-	switch (action.type) {
-		case "UNREADS_MARK_READ":
-			return {
-				...state,
-				[action.channel]: {
-					last_id: action.message,
-				},
-			};
-		case "UNREADS_SET": {
-			const obj: Unreads = {};
-			for (const entry of action.unreads) {
-				const { _id, ...v } = entry;
-				obj[_id.channel] = v;
-			}
+    switch (action.type) {
+        case "UNREADS_MARK_READ":
+            return {
+                ...state,
+                [action.channel]: {
+                    last_id: action.message,
+                },
+            };
+        case "UNREADS_SET": {
+            const obj: Unreads = {};
+            for (const entry of action.unreads) {
+                const { _id, ...v } = entry;
+                obj[_id.channel] = v;
+            }
 
-			return obj;
-		}
-		case "UNREADS_MENTION": {
-			const obj = state[action.channel];
+            return obj;
+        }
+        case "UNREADS_MENTION": {
+            const obj = state[action.channel];
 
-			return {
-				...state,
-				[action.channel]: {
-					...obj,
-					mentions: [...(obj?.mentions ?? []), action.message],
-				},
-			};
-		}
-		case "RESET":
-			return {};
-		default:
-			return state;
-	}
+            return {
+                ...state,
+                [action.channel]: {
+                    ...obj,
+                    mentions: [...(obj?.mentions ?? []), action.message],
+                },
+            };
+        }
+        case "RESET":
+            return {};
+        default:
+            return state;
+    }
 }
diff --git a/src/sw.ts b/src/sw.ts
index bf237fa..b2df5a9 100644
--- a/src/sw.ts
+++ b/src/sw.ts
@@ -7,14 +7,14 @@ import { precacheAndRoute } from "workbox-precaching";
 
 import type { State } from "./redux";
 import {
-	getNotificationState,
-	shouldNotify,
+    getNotificationState,
+    shouldNotify,
 } from "./redux/reducers/notifications";
 
 declare let self: ServiceWorkerGlobalScope;
 
 self.addEventListener("message", (event) => {
-	if (event.data && event.data.type === "SKIP_WAITING") self.skipWaiting();
+    if (event.data && event.data.type === "SKIP_WAITING") self.skipWaiting();
 });
 
 precacheAndRoute(self.__WB_MANIFEST);
@@ -26,150 +26,150 @@ const ENCODING_LEN = ENCODING.length;
 const TIME_LEN = 10;
 
 function decodeTime(id: string) {
-	var time = id
-		.substr(0, TIME_LEN)
-		.split("")
-		.reverse()
-		.reduce(function (carry, char, index) {
-			var encodingIndex = ENCODING.indexOf(char);
-			if (encodingIndex === -1) throw "invalid character found: " + char;
-
-			return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
-		}, 0);
-
-	return time;
+    var time = id
+        .substr(0, TIME_LEN)
+        .split("")
+        .reverse()
+        .reduce(function (carry, char, index) {
+            var encodingIndex = ENCODING.indexOf(char);
+            if (encodingIndex === -1) throw "invalid character found: " + char;
+
+            return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
+        }, 0);
+
+    return time;
 }
 
 self.addEventListener("push", (event) => {
-	async function process() {
-		if (event.data === null) return;
-		let data: Message = event.data.json();
-
-		let item = await localStorage.getItem("state");
-		if (!item) return;
-
-		const state: State = JSON.parse(item);
-		const autumn_url = state.config.features.autumn.url;
-		const user_id = state.auth.active!;
-
-		let db: IDBPDatabase;
-		try {
-			// Match RevoltClient.tsx#L55
-			db = await openDB("state", 3, {
-				upgrade(db) {
-					for (let store of [
-						"channels",
-						"servers",
-						"users",
-						"members",
-					]) {
-						db.createObjectStore(store, {
-							keyPath: "_id",
-						});
-					}
-				},
-			});
-		} catch (err) {
-			console.error(
-				"Failed to open IndexedDB store, continuing without.",
-			);
-			return;
-		}
-
-		async function get<T>(
-			store: string,
-			key: string,
-		): Promise<T | undefined> {
-			try {
-				return await db.get(store, key);
-			} catch (err) {
-				return undefined;
-			}
-		}
-
-		let channel = await get<Channel>("channels", data.channel);
-		let user = await get<User>("users", data.author);
-
-		if (channel) {
-			const notifs = getNotificationState(state.notifications, channel);
-			if (!shouldNotify(notifs, data, user_id)) return;
-		}
-
-		let title = `@${data.author}`;
-		let username = user?.username ?? data.author;
-		let image;
-		if (data.attachments) {
-			let attachment = data.attachments[0];
-			if (attachment.metadata.type === "Image") {
-				image = `${autumn_url}/${attachment.tag}/${attachment._id}`;
-			}
-		}
-
-		switch (channel?.channel_type) {
-			case "SavedMessages":
-				break;
-			case "DirectMessage":
-				title = `@${username}`;
-				break;
-			case "Group":
-				if (user?._id === "00000000000000000000000000") {
-					title = channel.name;
-				} else {
-					title = `@${user?.username} - ${channel.name}`;
-				}
-				break;
-			case "TextChannel":
-				{
-					let server = await get<Server>("servers", channel.server);
-					title = `@${user?.username} (#${channel.name}, ${server?.name})`;
-				}
-				break;
-		}
-
-		await self.registration.showNotification(title, {
-			icon: user?.avatar
-				? `${autumn_url}/${user.avatar.tag}/${user.avatar._id}`
-				: `https://api.revolt.chat/users/${data.author}/default_avatar`,
-			image,
-			body:
-				typeof data.content === "string"
-					? data.content
-					: JSON.stringify(data.content),
-			timestamp: decodeTime(data._id),
-			tag: data.channel,
-			badge: "https://app.revolt.chat/assets/icons/android-chrome-512x512.png",
-			data:
-				channel?.channel_type === "TextChannel"
-					? `/server/${channel.server}/channel/${channel._id}`
-					: `/channel/${data.channel}`,
-		});
-	}
-
-	event.waitUntil(process());
+    async function process() {
+        if (event.data === null) return;
+        let data: Message = event.data.json();
+
+        let item = await localStorage.getItem("state");
+        if (!item) return;
+
+        const state: State = JSON.parse(item);
+        const autumn_url = state.config.features.autumn.url;
+        const user_id = state.auth.active!;
+
+        let db: IDBPDatabase;
+        try {
+            // Match RevoltClient.tsx#L55
+            db = await openDB("state", 3, {
+                upgrade(db) {
+                    for (let store of [
+                        "channels",
+                        "servers",
+                        "users",
+                        "members",
+                    ]) {
+                        db.createObjectStore(store, {
+                            keyPath: "_id",
+                        });
+                    }
+                },
+            });
+        } catch (err) {
+            console.error(
+                "Failed to open IndexedDB store, continuing without.",
+            );
+            return;
+        }
+
+        async function get<T>(
+            store: string,
+            key: string,
+        ): Promise<T | undefined> {
+            try {
+                return await db.get(store, key);
+            } catch (err) {
+                return undefined;
+            }
+        }
+
+        let channel = await get<Channel>("channels", data.channel);
+        let user = await get<User>("users", data.author);
+
+        if (channel) {
+            const notifs = getNotificationState(state.notifications, channel);
+            if (!shouldNotify(notifs, data, user_id)) return;
+        }
+
+        let title = `@${data.author}`;
+        let username = user?.username ?? data.author;
+        let image;
+        if (data.attachments) {
+            let attachment = data.attachments[0];
+            if (attachment.metadata.type === "Image") {
+                image = `${autumn_url}/${attachment.tag}/${attachment._id}`;
+            }
+        }
+
+        switch (channel?.channel_type) {
+            case "SavedMessages":
+                break;
+            case "DirectMessage":
+                title = `@${username}`;
+                break;
+            case "Group":
+                if (user?._id === "00000000000000000000000000") {
+                    title = channel.name;
+                } else {
+                    title = `@${user?.username} - ${channel.name}`;
+                }
+                break;
+            case "TextChannel":
+                {
+                    let server = await get<Server>("servers", channel.server);
+                    title = `@${user?.username} (#${channel.name}, ${server?.name})`;
+                }
+                break;
+        }
+
+        await self.registration.showNotification(title, {
+            icon: user?.avatar
+                ? `${autumn_url}/${user.avatar.tag}/${user.avatar._id}`
+                : `https://api.revolt.chat/users/${data.author}/default_avatar`,
+            image,
+            body:
+                typeof data.content === "string"
+                    ? data.content
+                    : JSON.stringify(data.content),
+            timestamp: decodeTime(data._id),
+            tag: data.channel,
+            badge: "https://app.revolt.chat/assets/icons/android-chrome-512x512.png",
+            data:
+                channel?.channel_type === "TextChannel"
+                    ? `/server/${channel.server}/channel/${channel._id}`
+                    : `/channel/${data.channel}`,
+        });
+    }
+
+    event.waitUntil(process());
 });
 
 // ? Open the app on notification click.
 // https://stackoverflow.com/a/39457287
 self.addEventListener("notificationclick", function (event) {
-	let url = event.notification.data;
-	event.notification.close();
-	event.waitUntil(
-		self.clients
-			.matchAll({ includeUncontrolled: true, type: "window" })
-			.then((windowClients) => {
-				// Check if there is already a window/tab open with the target URL
-				for (var i = 0; i < windowClients.length; i++) {
-					var client = windowClients[i];
-					// If so, just focus it.
-					if (client.url === url && "focus" in client) {
-						return client.focus();
-					}
-				}
-
-				// If not, then open the target URL in a new window/tab.
-				if (self.clients.openWindow) {
-					return self.clients.openWindow(url);
-				}
-			}),
-	);
+    let url = event.notification.data;
+    event.notification.close();
+    event.waitUntil(
+        self.clients
+            .matchAll({ includeUncontrolled: true, type: "window" })
+            .then((windowClients) => {
+                // Check if there is already a window/tab open with the target URL
+                for (var i = 0; i < windowClients.length; i++) {
+                    var client = windowClients[i];
+                    // If so, just focus it.
+                    if (client.url === url && "focus" in client) {
+                        return client.focus();
+                    }
+                }
+
+                // If not, then open the target URL in a new window/tab.
+                if (self.clients.openWindow) {
+                    return self.clients.openWindow(url);
+                }
+            }),
+    );
 });
-- 
GitLab