diff --git a/external/lang b/external/lang
index eb880a90254df406b25caf6b969ed3aee3142f6c..0f98b07eb411ec5a9207536f6adc5aa8055e755e 160000
--- a/external/lang
+++ b/external/lang
@@ -1 +1 @@
-Subproject commit eb880a90254df406b25caf6b969ed3aee3142f6c
+Subproject commit 0f98b07eb411ec5a9207536f6adc5aa8055e755e
diff --git a/package.json b/package.json
index 406bd011e38353dd96925e30291d4709a5b02f01..0abe86cb403de3b845d5d063347b6d8789aa0c6e 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,7 @@
     "react-router-dom": "^5.2.0",
     "react-scroll": "^1.8.2",
     "redux": "^4.1.0",
-    "revolt.js": "5.0.0-alpha.11",
+    "revolt.js": "5.0.0-alpha.12",
     "rimraf": "^3.0.2",
     "sass": "^1.35.1",
     "shade-blend-color": "^1.0.0",
diff --git a/src/components/common/AutoComplete.tsx b/src/components/common/AutoComplete.tsx
index eab39a8c3b98cce4c768d3e4721ec02654e2d824..897b2956044d68725483219b463a09596144fe2a 100644
--- a/src/components/common/AutoComplete.tsx
+++ b/src/components/common/AutoComplete.tsx
@@ -105,7 +105,7 @@ export function useAutoComplete(
             const regex = new RegExp(search, "i");
 
             if (type === "emoji") {
-                // ! FIXME: we should convert it to a Binary Search Tree and use that
+                // ! TODO: we should convert it to a Binary Search Tree and use that
                 const matches = Object.keys(emojiDictionary)
                     .filter((emoji: string) => emoji.match(regex))
                     .splice(0, 5);
diff --git a/src/components/common/ChannelIcon.tsx b/src/components/common/ChannelIcon.tsx
index c020ec372620be30b7bd604814f930f7709145ba..6a13a0bc3b3e9c986ee77c50b5e3e8cf4c9218ec 100644
--- a/src/components/common/ChannelIcon.tsx
+++ b/src/components/common/ChannelIcon.tsx
@@ -50,7 +50,7 @@ export default observer(
         }
 
         return (
-            // ! fixme: replace fallback with <picture /> + <source />
+            // ! TODO: replace fallback with <picture /> + <source />
             <ImageIconBase
                 {...imgProps}
                 width={size}
diff --git a/src/components/common/messaging/Message.tsx b/src/components/common/messaging/Message.tsx
index ae9142408b24b291daf12f1bb6f9a6400a551e19..4ba001d67b40ddef51dcc3ae4b59c4b3470706f1 100644
--- a/src/components/common/messaging/Message.tsx
+++ b/src/components/common/messaging/Message.tsx
@@ -54,19 +54,19 @@ const Message = observer(
         const head =
             preferHead || (message.reply_ids && message.reply_ids.length > 0);
 
-        // ! FIXME: tell fatal to make this type generic
+        // ! TODO: tell fatal to make this type generic
         // bree: Fatal please...
         const userContext = attachContext
             ? (attachContextMenu("Menu", {
-                  user: message.author,
-                  contextualChannel: message.channel,
+                  user: message.author_id,
+                  contextualChannel: message.channel_id,
               }) as any)
             : undefined;
 
         const openProfile = () =>
             openScreen({ id: "profile", user_id: message.author_id });
 
-        // ! FIXME: animate on hover
+        // ! FIXME(?): animate on hover
         const [animate, setAnimate] = useState(false);
 
         return (
@@ -96,7 +96,7 @@ const Message = observer(
                         attachContext
                             ? attachContextMenu("Menu", {
                                   message,
-                                  contextualChannel: message.channel,
+                                  contextualChannel: message.channel_id,
                                   queued,
                               })
                             : undefined
diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx
index b74c98bd1d36447cd28b1c28e73edc37ee72665c..78a1b1a7fe9558ceff7672ca64752c70d954d716 100644
--- a/src/components/common/messaging/MessageBox.tsx
+++ b/src/components/common/messaging/MessageBox.tsx
@@ -191,8 +191,7 @@ export default function MessageBox({ channel }: Props) {
         playSound("outbound");
 
         const nonce = ulid();
-        // ! FIXME: queued
-        /*dispatch({
+        dispatch({
             type: "QUEUE_ADD",
             nonce,
             channel: channel._id,
@@ -204,7 +203,7 @@ export default function MessageBox({ channel }: Props) {
                 content,
                 replies,
             },
-        });*/
+        });
 
         defer(() =>
             SingletonMessageRenderer.jumpToBottom(
diff --git a/src/components/navigation/items/ButtonItem.tsx b/src/components/navigation/items/ButtonItem.tsx
index 9372c0f05fe588e5be5a11661f1018661c2943a5..f6d64a489f8a7b082a7115bc0431c2ba38b8bd97 100644
--- a/src/components/navigation/items/ButtonItem.tsx
+++ b/src/components/navigation/items/ButtonItem.tsx
@@ -136,7 +136,7 @@ export const ChannelButton = observer((props: ChannelProps) => {
             {...divProps}
             data-active={active}
             data-alert={typeof alert === "string"}
-            aria-label={{}} /*FIXME: ADD ARIA LABEL*/
+            aria-label={channel.name}
             className={classNames(styles.item, { [styles.compact]: compact })}
             onContextMenu={attachContextMenu("Menu", {
                 channel: channel._id,
diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx
index 4306697eda64587db1f13b7bf51bb24251070a58..d5a472347c60cb542a2dd78d85469d1264565ac6 100644
--- a/src/components/ui/Modal.tsx
+++ b/src/components/ui/Modal.tsx
@@ -190,7 +190,7 @@ export default function Modal(props: Props) {
     useEffect(() => {
         if (!confirmationAction) return;
 
-        // ! FIXME: this may be done better if we
+        // ! TODO: this may be done better if we
         // ! can focus the button although that
         // ! doesn't seem to work...
         function keyDown(e: KeyboardEvent) {
diff --git a/src/context/Voice.tsx b/src/context/Voice.tsx
index 43fbb9df1110483cd59d832e6cea2c500d5aa075..9c44a0c225e1791c15e89e3787bad6af5b058626 100644
--- a/src/context/Voice.tsx
+++ b/src/context/Voice.tsx
@@ -95,7 +95,7 @@ export default function Voice({ children }: Props) {
                         return channel;
                     }
 
-                    // ! FIXME: use configuration to check if voso is enabled
+                    // ! TODO: use configuration to check if voso is enabled
                     // await client.connect("wss://voso.revolt.chat/ws");
                     await client.connect(
                         "wss://voso.revolt.chat/ws",
@@ -138,9 +138,9 @@ export default function Voice({ children }: Props) {
                 switch (type) {
                     case "audio": {
                         if (client?.audioProducer !== undefined)
-                            return console.log("No audio producer."); // ! FIXME: let the user know
+                            return console.log("No audio producer."); // ! TODO: let the user know
                         if (navigator.mediaDevices === undefined)
-                            return console.log("No media devices."); // ! FIXME: let the user know
+                            return console.log("No media devices."); // ! TODO: let the user know
                         const mediaStream =
                             await navigator.mediaDevices.getUserMedia({
                                 audio: true,
@@ -165,7 +165,7 @@ export default function Voice({ children }: Props) {
     useEffect(() => {
         if (!client?.supported()) return;
 
-        // ! FIXME: message for fatal:
+        // ! TODO: message for fatal:
         // ! get rid of these force updates
         // ! handle it through state or smth
 
diff --git a/src/context/intermediate/popovers/UserPicker.module.scss b/src/context/intermediate/popovers/UserPicker.module.scss
index 56afcf32253d95b4b51b1f5c3c8b6d3d13ab73ac..610de5a7ec568b3ef4072a764ac2ee6cc2982f09 100644
--- a/src/context/intermediate/popovers/UserPicker.module.scss
+++ b/src/context/intermediate/popovers/UserPicker.module.scss
@@ -4,7 +4,6 @@
     max-height: 360px;
     overflow-y: scroll;
 
-    // ! FIXME: very temporary code
     > label {
         > span {
             align-items: flex-start !important;
@@ -18,4 +17,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/context/revoltjs/Notifications.tsx b/src/context/revoltjs/Notifications.tsx
index 719a3341ee00cfc2e23b6060b037b9f9ef6c843d..b21b6af10befebeee2579fa466e546ba5a90aec0 100644
--- a/src/context/revoltjs/Notifications.tsx
+++ b/src/context/revoltjs/Notifications.tsx
@@ -1,8 +1,9 @@
-import { autorun } from "mobx";
+import { autorun, reaction } from "mobx";
 import { Route, Switch, useHistory, useParams } from "react-router-dom";
 import { Presence, RelationshipStatus } from "revolt-api/types/Users";
 import { SYSTEM_USER_ID } from "revolt.js";
 import { Message } from "revolt.js/dist/maps/Messages";
+import { User } from "revolt.js/dist/maps/Users";
 import { decodeTime } from "ulid";
 
 import { useContext, useEffect } from "preact/hooks";
@@ -206,19 +207,18 @@ function Notifier({ options, notifs }: Props) {
         }
     }
 
-    /*async function relationship(user: User, property: string) {
-        if (client.user?.status?.presence === Users.Presence.Busy) return;
-        if (property !== "relationship") return;
+    async function relationship(user: User) {
+        if (client.user?.status?.presence === Presence.Busy) return;
         if (!showNotification) return;
 
         let event;
         switch (user.relationship) {
-            case Users.Relationship.Incoming:
+            case RelationshipStatus.Incoming:
                 event = translate("notifications.sent_request", {
                     person: user.username,
                 });
                 break;
-            case Users.Relationship.Friend:
+            case RelationshipStatus.Friend:
                 event = translate("notifications.now_friends", {
                     person: user.username,
                 });
@@ -236,17 +236,15 @@ function Notifier({ options, notifs }: Props) {
         notif?.addEventListener("click", () => {
             history.push(`/friends`);
         });
-    }*/
+    }
 
     useEffect(() => {
-        // ! FIXME: need event from client about relationship
-
         client.addListener("message", message);
-        // client.users.addListener("mutation", relationship);
+        client.addListener("user/relationship", relationship);
 
         return () => {
             client.removeListener("message", message);
-            // client.users.removeListener("mutation", relationship);
+            client.removeListener("user/relationship", relationship);
         };
     }, [client, playSound, guild_id, channel_id, showNotification, notifs]);
 
diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx
index b89b7e88a78d26a9aaf423af6e6f7137b8fe7b54..5a3c4539b440fa15d4be5cc2537140f627daa1b4 100644
--- a/src/lib/ContextMenus.tsx
+++ b/src/lib/ContextMenus.tsx
@@ -782,11 +782,15 @@ function ContextMenus(props: Props) {
                                     break;
                                 case "TextChannel":
                                 case "VoiceChannel":
-                                    // ! FIXME: add permission for invites
-                                    generateAction({
-                                        action: "create_invite",
-                                        target: channel,
-                                    });
+                                    if (
+                                        channelPermissions &
+                                        ChannelPermission.InviteOthers
+                                    ) {
+                                        generateAction({
+                                            action: "create_invite",
+                                            target: channel,
+                                        });
+                                    }
 
                                     if (
                                         serverPermissions &
diff --git a/src/pages/channels/messaging/MessageRenderer.tsx b/src/pages/channels/messaging/MessageRenderer.tsx
index c30d14d8ee9a0a9f7e7ed04afc30d60be3b1b452..94f771e3cc63fd445f4b8e596f83ef1af4ea5ea2 100644
--- a/src/pages/channels/messaging/MessageRenderer.tsx
+++ b/src/pages/channels/messaging/MessageRenderer.tsx
@@ -2,6 +2,7 @@ import { X } from "@styled-icons/boxicons-regular";
 import { RelationshipStatus } from "revolt-api/types/Users";
 import { SYSTEM_USER_ID } from "revolt.js";
 import { Message as MessageObject } from "revolt.js/dist/maps/Messages";
+import { Message as MessageI } from "revolt.js/dist/maps/Messages";
 import styled from "styled-components";
 import { decodeTime } from "ulid";
 
@@ -16,7 +17,7 @@ import { connectState } from "../../../redux/connector";
 import { QueuedMessage } from "../../../redux/reducers/queue";
 
 import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
-import { AppContext } from "../../../context/revoltjs/RevoltClient";
+import { AppContext, useClient } from "../../../context/revoltjs/RevoltClient";
 
 import Message from "../../../components/common/messaging/Message";
 import { SystemMessage } from "../../../components/common/messaging/SystemMessage";
@@ -48,7 +49,7 @@ const BlockedMessage = styled.div`
 function MessageRenderer({ id, state, queue, highlight }: Props) {
     if (state.type !== "RENDER") return null;
 
-    const client = useContext(AppContext);
+    const client = useClient();
     const userId = client.user!._id;
 
     const [editing, setEditing] = useState<string | undefined>(undefined);
@@ -148,7 +149,6 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
                 />,
             );
         } else {
-            // ! FIXME: temp solution
             if (message.author?.relationship === RelationshipStatus.Blocked) {
                 blocked++;
             } else {
@@ -190,23 +190,24 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
 
                 previous = {
                     _id: msg.id,
-                    data: { author: userId! },
+                    author_id: userId!,
                 } as any;
             }
 
-            // ! FIXME: add queued messages back
-            /* render.push(
+            render.push(
                 <Message
-                    message={{
-                        ...msg.data,
-                        replies: msg.data.replies.map((x) => x.id),
-                    }}
+                    message={
+                        new MessageI(client, {
+                            ...msg.data,
+                            replies: msg.data.replies.map((x) => x.id),
+                        })
+                    }
                     key={msg.id}
                     queued={msg}
                     head={head}
                     attachContext
                 />,
-            ); */
+            );
         }
     } else {
         render.push(
diff --git a/src/pages/invite/Invite.tsx b/src/pages/invite/Invite.tsx
index 056a464afa5164f43600a616a3fe8374899a1dea..42093065b40d987fd12294f172ee04517904915c 100644
--- a/src/pages/invite/Invite.tsx
+++ b/src/pages/invite/Invite.tsx
@@ -4,9 +4,11 @@ import { useHistory, useParams } from "react-router-dom";
 import { RetrievedInvite } from "revolt-api/types/Invites";
 
 import styles from "./Invite.module.scss";
+import { Text } from "preact-i18n";
 import { useContext, useEffect, useState } from "preact/hooks";
 
 import { defer } from "../../lib/defer";
+import { TextReact } from "../../lib/i18n";
 
 import RequiresOnline from "../../context/revoltjs/RequiresOnline";
 import {
@@ -90,12 +92,20 @@ export default function Invite() {
                         <h1>{invite.server_name}</h1>
                         <h2>#{invite.channel_name}</h2>
                         <h3>
-                            Invited by{" "}
-                            <UserIcon
-                                size={24}
-                                attachment={invite.user_avatar}
-                            />{" "}
-                            {invite.user_name}
+                            <TextReact
+                                id="app.special.invite.invited_by"
+                                fields={{
+                                    user: (
+                                        <>
+                                            <UserIcon
+                                                size={24}
+                                                attachment={invite.user_avatar}
+                                            />{" "}
+                                            {invite.user_name}
+                                        </>
+                                    ),
+                                }}
+                            />
                         </h3>
                         <Overline type="error" error={error} />
                         <Button
@@ -140,9 +150,11 @@ export default function Invite() {
                                     setProcessing(false);
                                 }
                             }}>
-                            {status === ClientStatus.READY
-                                ? "Login to Revolt"
-                                : "Accept Invite"}
+                            {status === ClientStatus.READY ? (
+                                <Text id="app.special.invite.login" />
+                            ) : (
+                                <Text id="app.special.invite.accept" />
+                            )}
                         </Button>
                     </>
                 )}
diff --git a/src/pages/settings/channel/Permissions.tsx b/src/pages/settings/channel/Permissions.tsx
index 54d670538d55c50c3a13139e1711280e58a982f6..156708ee67891f974127025504281129316fa80f 100644
--- a/src/pages/settings/channel/Permissions.tsx
+++ b/src/pages/settings/channel/Permissions.tsx
@@ -1,25 +1,18 @@
 import { observer } from "mobx-react-lite";
-import { ChannelPermission } from "revolt.js/dist/api/permissions";
+import {
+    ChannelPermission,
+    DEFAULT_PERMISSION_DM,
+} from "revolt.js/dist/api/permissions";
 import { Channel } from "revolt.js/dist/maps/Channels";
 
-import { useContext, useEffect, useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
 
-import { AppContext, useClient } from "../../../context/revoltjs/RevoltClient";
+import { useClient } from "../../../context/revoltjs/RevoltClient";
 
 import Button from "../../../components/ui/Button";
 import Checkbox from "../../../components/ui/Checkbox";
 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;
-
 interface Props {
     channel: Channel;
 }
@@ -27,7 +20,6 @@ interface Props {
 // ! FIXME: bad code :)
 export default observer(({ channel }: Props) => {
     const [selected, setSelected] = useState("default");
-    const client = useClient();
 
     type R = { name: string; permissions: number };
     const roles: { [key: string]: R } = {};
diff --git a/src/redux/reducers/queue.ts b/src/redux/reducers/queue.ts
index ea3437af436616a54ae35536879abdad71cd6bc0..c4059e053c5bb564961b879a847790e1392372d2 100644
--- a/src/redux/reducers/queue.ts
+++ b/src/redux/reducers/queue.ts
@@ -9,6 +9,10 @@ export interface Reply {
 }
 
 export type QueuedMessageData = {
+    _id: string;
+    author: string;
+    channel: string;
+
     content: string;
     replies: Reply[];
 };
diff --git a/yarn.lock b/yarn.lock
index 80442694d5134a805b889583ce516a686dd07ece..f95d6a072060ab94aa70dd7de45cccc0db9c8c76 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3570,10 +3570,10 @@ revolt-api@0.5.1-alpha.10-patch.0:
   resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.10-patch.0.tgz#97d31bec7dfa4573567097443acb059c4feaac20"
   integrity sha512-UyM890HkGlYNQOxpHuEpUsJHLt8Ujnjg9/zPEDGpbvS4iy0jmHX23Hh8tOCfb/ewxbNrtT3G1HpSWKOneW/vYg==
 
-revolt.js@5.0.0-alpha.11:
-  version "5.0.0-alpha.11"
-  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.11.tgz#af131150ce37a39b979501c730ec2bee5a4e38a1"
-  integrity sha512-aOD8IV2DM9Ebq15FVQ2GJDGBaICppk+jP06dW0KlrDeVoE3Hra5CRLHSIRtp2mJN/vh1cH+9eA78jssAlAgwAw==
+revolt.js@5.0.0-alpha.12:
+  version "5.0.0-alpha.12"
+  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-5.0.0-alpha.12.tgz#06777f74f6a79161b18e02938a8d60d465395066"
+  integrity sha512-QqgawsSjrFTKhMA5JBKii6MgOJ4VO2u1GBmjTiKzPR2krnoWHdPSbD7VvD2scMGMPXIaIxU3zA++tyN8mfzIFg==
   dependencies:
     axios "^0.19.2"
     eventemitter3 "^4.0.7"