Feature: Add message links.

This commit is contained in:
Paul 2021-07-08 22:47:56 +01:00
parent 4474d2ec56
commit c54fe0f1bf
8 changed files with 78 additions and 27 deletions

2
external/lang vendored

@ -1 +1 @@
Subproject commit b18d44b56037d09bd2fac68be04e42723e50a3d7 Subproject commit 846a0e8a3a36e606d4d4249e495dc0daeee9c65d

View file

@ -94,7 +94,7 @@
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scroll": "^1.8.2", "react-scroll": "^1.8.2",
"redux": "^4.1.0", "redux": "^4.1.0",
"revolt.js": "4.3.3-alpha.10", "revolt.js": "4.3.3-alpha.11",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "^1.35.1", "sass": "^1.35.1",
"shade-blend-color": "^1.0.0", "shade-blend-color": "^1.0.0",

View file

@ -72,11 +72,11 @@ export class SingletonRenderer extends EventEmitter3 {
this.stale = true; this.stale = true;
} }
async init(id: string) { async init(id: string, message_id?: string) {
this.channel = id; this.channel = id;
this.stale = false; this.stale = false;
this.setStateUnguarded({ type: "LOADING" }); this.setStateUnguarded({ type: "LOADING" });
await this.currentRenderer.init(this, id); await this.currentRenderer.init(this, id, message_id);
} }
async reloadStale(id: string) { async reloadStale(id: string) {
@ -180,7 +180,7 @@ export class SingletonRenderer extends EventEmitter3 {
if (this.state.type === "RENDER" && this.state.atBottom) { if (this.state.type === "RENDER" && this.state.atBottom) {
this.emit("scroll", { type: "ScrollToBottom", smooth }); this.emit("scroll", { type: "ScrollToBottom", smooth });
} else { } else {
await this.currentRenderer.init(this, id, true); await this.currentRenderer.init(this, id, undefined, true);
} }
} }
} }

View file

@ -4,24 +4,42 @@ import { SMOOTH_SCROLL_ON_RECEIVE } from "../Singleton";
import { RendererRoutines } from "../types"; import { RendererRoutines } from "../types";
export const SimpleRenderer: RendererRoutines = { export const SimpleRenderer: RendererRoutines = {
init: async (renderer, id, smooth) => { init: async (renderer, id, nearby, smooth) => {
if (renderer.client!.websocket.connected) { if (renderer.client!.websocket.connected) {
renderer if (nearby)
.client!.channels.fetchMessagesWithUsers(id, {}, true) renderer
.then(({ messages: data }) => { .client!.channels.fetchMessagesWithUsers(id, { nearby, limit: 100 }, true)
data.reverse(); .then(({ messages: data }) => {
let messages = data.map((x) => mapMessage(x)); data.sort((a, b) => a._id.localeCompare(b._id));
renderer.setState( let messages = data.map((x) => mapMessage(x));
id, renderer.setState(
{ id,
type: "RENDER", {
messages, type: "RENDER",
atTop: data.length < 50, messages,
atBottom: true, atTop: false,
}, atBottom: false,
{ type: "ScrollToBottom", smooth }, },
); { 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 { } else {
renderer.setState(id, { type: "WAITING_FOR_NETWORK" }); renderer.setState(id, { type: "WAITING_FOR_NETWORK" });
} }

View file

@ -8,6 +8,7 @@ export type ScrollState =
| { type: "Free" } | { type: "Free" }
| { type: "Bottom"; scrollingUntil?: number } | { type: "Bottom"; scrollingUntil?: number }
| { type: "ScrollToBottom" | "StayAtBottom"; smooth?: boolean } | { type: "ScrollToBottom" | "StayAtBottom"; smooth?: boolean }
| { type: "ScrollToView", id: string }
| { type: "OffsetTop"; previousHeight: number } | { type: "OffsetTop"; previousHeight: number }
| { type: "ScrollTop"; y: number }; | { type: "ScrollTop"; y: number };
@ -26,6 +27,7 @@ export interface RendererRoutines {
init: ( init: (
renderer: SingletonRenderer, renderer: SingletonRenderer,
id: string, id: string,
message?: string,
smooth?: boolean, smooth?: boolean,
) => Promise<void>; ) => Promise<void>;

View file

@ -90,9 +90,14 @@ export default function App() {
/> />
<Route <Route
path="/channel/:channel/message/:message" path="/channel/:channel/:message"
component={Channel} component={Channel}
/> />
<Route
path="/server/:server/channel/:channel/:message"
component={Channel}
/>
<Route <Route
path="/server/:server/channel/:channel" path="/server/:server/channel/:channel"
component={Channel} component={Channel}

View file

@ -19,6 +19,7 @@ import { RenderState, ScrollState } from "../../../lib/renderer/types";
import { IntermediateContext } from "../../../context/intermediate/Intermediate"; import { IntermediateContext } from "../../../context/intermediate/Intermediate";
import RequiresOnline from "../../../context/revoltjs/RequiresOnline"; import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
import { import {
AppContext,
ClientStatus, ClientStatus,
StatusContext, StatusContext,
} from "../../../context/revoltjs/RevoltClient"; } from "../../../context/revoltjs/RevoltClient";
@ -27,6 +28,7 @@ import Preloader from "../../../components/ui/Preloader";
import ConversationStart from "./ConversationStart"; import ConversationStart from "./ConversationStart";
import MessageRenderer from "./MessageRenderer"; import MessageRenderer from "./MessageRenderer";
import { useHistory, useParams } from "react-router-dom";
const Area = styled.div` const Area = styled.div`
height: 100%; height: 100%;
@ -53,9 +55,13 @@ export const MessageAreaWidthContext = createContext(0);
export const MESSAGE_AREA_PADDING = 82; export const MESSAGE_AREA_PADDING = 82;
export function MessageArea({ id }: Props) { export function MessageArea({ id }: Props) {
const history = useHistory();
const client = useContext(AppContext);
const status = useContext(StatusContext); const status = useContext(StatusContext);
const { focusTaken } = useContext(IntermediateContext); const { focusTaken } = useContext(IntermediateContext);
const { message } = useParams<{ message: string }>();
// ? This is the scroll container. // ? This is the scroll container.
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver<HTMLDivElement>({ ref }); const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
@ -91,6 +97,11 @@ export function MessageArea({ id }: Props) {
container: ref.current, container: ref.current,
duration: scrollState.current.smooth ? 150 : 0, 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") { } else if (scrollState.current.type === "OffsetTop") {
animateScroll.scrollTo( animateScroll.scrollTo(
Math.max( Math.max(
@ -152,9 +163,24 @@ export function MessageArea({ id }: Props) {
// ? Load channel initially. // ? Load channel initially.
useEffect(() => { useEffect(() => {
if (message) return;
SingletonMessageRenderer.init(id); SingletonMessageRenderer.init(id);
}, [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. // ? If we are waiting for network, try again.
useEffect(() => { useEffect(() => {
switch (status) { switch (status) {

View file

@ -3563,10 +3563,10 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
revolt.js@4.3.3-alpha.10: revolt.js@4.3.3-alpha.11:
version "4.3.3-alpha.10" version "4.3.3-alpha.11"
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.10.tgz#3acbdd4f44c7f12be53faa0318396ce21694acde" resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.3-alpha.11.tgz#d8499607cc3de48ab580c7da9bf2e8e0a1992ae9"
integrity sha512-s9VEJX1LBiHCl8mXyqD0+GnIQg6WJj7CV8vUAO6Rv35Jwy0gOjjOvma4csXeZTdiLpPoVFxutgBj8bXMnVL5Aw== integrity sha512-//UB+VXKE4MziUoBm2RMDZNgxLmrar8FQ1hCewkdFbTdfkYey6joagNVF5DW+ImbffYbrnnjZhlJP7RRXZ5IeQ==
dependencies: dependencies:
"@insertish/mutable" "1.1.0" "@insertish/mutable" "1.1.0"
axios "^0.19.2" axios "^0.19.2"