Skip to content
Snippets Groups Projects
MessageRenderer.tsx 5.22 KiB
Newer Older
import { decodeTime } from "ulid";
insert's avatar
insert committed
import MessageEditor from "./MessageEditor";
import { Children } from "../../../types/Preact";
import { useEffect, useState } from "preact/hooks";
import ConversationStart from "./ConversationStart";
import { connectState } from "../../../redux/connector";
import Preloader from "../../../components/ui/Preloader";
import { RenderState } from "../../../lib/renderer/types";
import DateDivider from "../../../components/ui/DateDivider";
import { QueuedMessage } from "../../../redux/reducers/queue";
import { MessageObject } from "../../../context/revoltjs/util";
insert's avatar
insert committed
import Message from "../../../components/common/messaging/Message";
import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
import { useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
insert's avatar
insert committed
import { internalSubscribe, internalEmit } from "../../../lib/eventEmitter";
import { SystemMessage } from "../../../components/common/messaging/SystemMessage";

interface Props {
    id: string;
    state: RenderState;
    queue: QueuedMessage[];
}

function MessageRenderer({ id, state, queue }: Props) {
    if (state.type !== 'RENDER') return null;

    const ctx = useForceUpdate();
    const users = useUsers();
    const userId = ctx.client.user!._id;

    /*
    const view = useView(id);*/

    const [editing, setEditing] = useState<string | undefined>(undefined);
    const stopEditing = () => {
        setEditing(undefined);
insert's avatar
insert committed
        internalEmit("MessageBox", "focus");
    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;
                }
            }
        }

insert's avatar
insert committed
        const subs = [
            internalSubscribe("MessageRenderer", "edit_last", editLast),
            internalSubscribe("MessageRenderer", "edit_message", setEditing)
        ]
insert's avatar
insert committed
        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 />
            </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 = curAuthor !== prevAuthor || Math.abs(btime - atime) >= 420000;
    }

    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 {
            render.push(
                <Message message={message}
                    key={message._id}
                    head={head}
insert's avatar
insert committed
                    content={
                        editing === message._id ?
                            <MessageEditor message={message} finish={stopEditing} />
                            : undefined
                    }
                    attachContext />
            );
        }

        previous = message;
    }

    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,
                    previous._id,
                    previous.author
                );
                
                previous = {
                    _id: msg.id,
                    data: { author: userId! }
                } as any;
            }

            /*render.push(
                <Message
                    user={users.find(x => x?._id === userId)}
                    message={msg.data}
                    queued={msg}
                    key={msg.id}
                    head={head}
                />
            );*/
            render.push(
                <Message message={msg.data}
                    key={msg.id}
                    head={head}
                    attachContext />
            );
        }

        // render.push(<div>end</div>);
    } else {
        render.push(
            <RequiresOnline>
                <Preloader />
            </RequiresOnline>
        );
    }

    return <>{ render }</>;
}

export default connectState<Omit<Props, 'queue'>>(MessageRenderer, state => {
    return {
        queue: state.queue
    };
});