From c54fe0f1bf449b24eca093f1fdd12664447cd2de Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Thu, 8 Jul 2021 22:47:56 +0100
Subject: [PATCH] Feature: Add message links.

---
 external/lang                                |  2 +-
 package.json                                 |  2 +-
 src/lib/renderer/Singleton.ts                |  6 +--
 src/lib/renderer/simple/SimpleRenderer.ts    | 52 +++++++++++++-------
 src/lib/renderer/types.ts                    |  2 +
 src/pages/RevoltApp.tsx                      |  7 ++-
 src/pages/channels/messaging/MessageArea.tsx | 26 ++++++++++
 yarn.lock                                    |  8 +--
 8 files changed, 78 insertions(+), 27 deletions(-)

diff --git a/external/lang b/external/lang
index b18d44b..846a0e8 160000
--- a/external/lang
+++ b/external/lang
@@ -1 +1 @@
-Subproject commit b18d44b56037d09bd2fac68be04e42723e50a3d7
+Subproject commit 846a0e8a3a36e606d4d4249e495dc0daeee9c65d
diff --git a/package.json b/package.json
index 0ca7a34..becf247 100644
--- a/package.json
+++ b/package.json
@@ -94,7 +94,7 @@
     "react-router-dom": "^5.2.0",
     "react-scroll": "^1.8.2",
     "redux": "^4.1.0",
-    "revolt.js": "4.3.3-alpha.10",
+    "revolt.js": "4.3.3-alpha.11",
     "rimraf": "^3.0.2",
     "sass": "^1.35.1",
     "shade-blend-color": "^1.0.0",
diff --git a/src/lib/renderer/Singleton.ts b/src/lib/renderer/Singleton.ts
index f95e8df..83bb2ef 100644
--- a/src/lib/renderer/Singleton.ts
+++ b/src/lib/renderer/Singleton.ts
@@ -72,11 +72,11 @@ export class SingletonRenderer extends EventEmitter3 {
         this.stale = true;
     }
 
-    async init(id: string) {
+    async init(id: string, message_id?: string) {
         this.channel = id;
         this.stale = false;
         this.setStateUnguarded({ type: "LOADING" });
-        await this.currentRenderer.init(this, id);
+        await this.currentRenderer.init(this, id, message_id);
     }
 
     async reloadStale(id: string) {
@@ -180,7 +180,7 @@ export class SingletonRenderer extends EventEmitter3 {
         if (this.state.type === "RENDER" && this.state.atBottom) {
             this.emit("scroll", { type: "ScrollToBottom", smooth });
         } else {
-            await this.currentRenderer.init(this, id, true);
+            await this.currentRenderer.init(this, id, undefined, true);
         }
     }
 }
diff --git a/src/lib/renderer/simple/SimpleRenderer.ts b/src/lib/renderer/simple/SimpleRenderer.ts
index 05feadc..00e91d6 100644
--- a/src/lib/renderer/simple/SimpleRenderer.ts
+++ b/src/lib/renderer/simple/SimpleRenderer.ts
@@ -4,24 +4,42 @@ import { SMOOTH_SCROLL_ON_RECEIVE } from "../Singleton";
 import { RendererRoutines } from "../types";
 
 export const SimpleRenderer: RendererRoutines = {
-    init: async (renderer, id, smooth) => {
+    init: async (renderer, id, nearby, 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 },
-                    );
-                });
+            if (nearby)
+                renderer
+                    .client!.channels.fetchMessagesWithUsers(id, { nearby, limit: 100 }, true)
+                    .then(({ messages: data }) => {
+                        data.sort((a, b) => a._id.localeCompare(b._id));
+                        let messages = data.map((x) => mapMessage(x));
+                        renderer.setState(
+                            id,
+                            {
+                                type: "RENDER",
+                                messages,
+                                atTop: false,
+                                atBottom: false,
+                            },
+                            { type: "ScrollToView", id: nearby },
+                        );
+                    });
+            else
+                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" });
         }
diff --git a/src/lib/renderer/types.ts b/src/lib/renderer/types.ts
index e066925..6674536 100644
--- a/src/lib/renderer/types.ts
+++ b/src/lib/renderer/types.ts
@@ -8,6 +8,7 @@ export type ScrollState =
     | { type: "Free" }
     | { type: "Bottom"; scrollingUntil?: number }
     | { type: "ScrollToBottom" | "StayAtBottom"; smooth?: boolean }
+    | { type: "ScrollToView", id: string }
     | { type: "OffsetTop"; previousHeight: number }
     | { type: "ScrollTop"; y: number };
 
@@ -26,6 +27,7 @@ export interface RendererRoutines {
     init: (
         renderer: SingletonRenderer,
         id: string,
+        message?: string,
         smooth?: boolean,
     ) => Promise<void>;
 
diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx
index d5137d8..446494d 100644
--- a/src/pages/RevoltApp.tsx
+++ b/src/pages/RevoltApp.tsx
@@ -90,9 +90,14 @@ export default function App() {
                     />
 
                     <Route
-                        path="/channel/:channel/message/:message"
+                        path="/channel/:channel/:message"
                         component={Channel}
                     />
+                    <Route
+                        path="/server/:server/channel/:channel/:message"
+                        component={Channel}
+                    />
+
                     <Route
                         path="/server/:server/channel/:channel"
                         component={Channel}
diff --git a/src/pages/channels/messaging/MessageArea.tsx b/src/pages/channels/messaging/MessageArea.tsx
index 23675f4..5f5f439 100644
--- a/src/pages/channels/messaging/MessageArea.tsx
+++ b/src/pages/channels/messaging/MessageArea.tsx
@@ -19,6 +19,7 @@ import { RenderState, ScrollState } from "../../../lib/renderer/types";
 import { IntermediateContext } from "../../../context/intermediate/Intermediate";
 import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
 import {
+    AppContext,
     ClientStatus,
     StatusContext,
 } from "../../../context/revoltjs/RevoltClient";
@@ -27,6 +28,7 @@ import Preloader from "../../../components/ui/Preloader";
 
 import ConversationStart from "./ConversationStart";
 import MessageRenderer from "./MessageRenderer";
+import { useHistory, useParams } from "react-router-dom";
 
 const Area = styled.div`
     height: 100%;
@@ -53,9 +55,13 @@ export const MessageAreaWidthContext = createContext(0);
 export const MESSAGE_AREA_PADDING = 82;
 
 export function MessageArea({ id }: Props) {
+    const history = useHistory();
+    const client = useContext(AppContext);
     const status = useContext(StatusContext);
     const { focusTaken } = useContext(IntermediateContext);
 
+    const { message } = useParams<{ message: string }>();
+
     // ? This is the scroll container.
     const ref = useRef<HTMLDivElement>(null);
     const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
@@ -91,6 +97,11 @@ export function MessageArea({ id }: Props) {
                     container: ref.current,
                     duration: scrollState.current.smooth ? 150 : 0,
                 });
+            } else if (scrollState.current.type === "ScrollToView") {
+                document.getElementById(scrollState.current.id)
+                    ?.scrollIntoView();
+                
+                setScrollState({ type: "Free" });
             } else if (scrollState.current.type === "OffsetTop") {
                 animateScroll.scrollTo(
                     Math.max(
@@ -152,9 +163,24 @@ export function MessageArea({ id }: Props) {
 
     // ? Load channel initially.
     useEffect(() => {
+        if (message) return;
         SingletonMessageRenderer.init(id);
     }, [id]);
 
+    // ? If message present or changes, load it as well.
+    useEffect(() => {
+        if (message) {
+            SingletonMessageRenderer.init(id, message);
+
+            let channel = client.channels.get(id);
+            if (channel?.channel_type === 'TextChannel') {
+                history.push(`/server/${channel.server}/channel/${id}`);
+            } else {
+                history.push(`/channel/${id}`);
+            }
+        }
+    }, [message]);
+
     // ? If we are waiting for network, try again.
     useEffect(() => {
         switch (status) {
diff --git a/yarn.lock b/yarn.lock
index f1c0139..870786d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3563,10 +3563,10 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
-revolt.js@4.3.3-alpha.10:
-  version "4.3.3-alpha.10"
-  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.10.tgz#3acbdd4f44c7f12be53faa0318396ce21694acde"
-  integrity sha512-s9VEJX1LBiHCl8mXyqD0+GnIQg6WJj7CV8vUAO6Rv35Jwy0gOjjOvma4csXeZTdiLpPoVFxutgBj8bXMnVL5Aw==
+revolt.js@4.3.3-alpha.11:
+  version "4.3.3-alpha.11"
+  resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.11.tgz#d8499607cc3de48ab580c7da9bf2e8e0a1992ae9"
+  integrity sha512-//UB+VXKE4MziUoBm2RMDZNgxLmrar8FQ1hCewkdFbTdfkYey6joagNVF5DW+ImbffYbrnnjZhlJP7RRXZ5IeQ==
   dependencies:
     "@insertish/mutable" "1.1.0"
     axios "^0.19.2"
-- 
GitLab