Skip to content
Snippets Groups Projects
ServerSidebar.tsx 3.67 KiB
Newer Older
insert's avatar
insert committed
import { Redirect, useParams } from "react-router";
import { ChannelButton } from "../items/ButtonItem";
import { Channels } from "revolt.js/dist/api/objects";
import { Unreads } from "../../../redux/reducers/unreads";
import { WithDispatcher } from "../../../redux/reducers";
insert's avatar
insert committed
import { useChannels, useForceUpdate, useServer } from "../../../context/revoltjs/hooks";
insert's avatar
insert committed
import { mapChannelWithUnread, useUnreads } from "./common";
import ConnectionStatus from '../items/ConnectionStatus';
import { connectState } from "../../../redux/connector";
import PaintCounter from "../../../lib/PaintCounter";
insert's avatar
insert committed
import styled from "styled-components";
import { attachContextMenu } from 'preact-context-menu';
insert's avatar
insert committed
import ServerHeader from "../../common/ServerHeader";
insert's avatar
insert committed
import { useEffect } from "preact/hooks";
insert's avatar
insert committed
import Category from "../../ui/Category";
insert's avatar
insert committed
import ConditionalLink from "../../../lib/ConditionalLink";
insert's avatar
insert committed

interface Props {
    unreads: Unreads;
}

insert's avatar
insert committed
const ServerBase = styled.div`
    height: 100%;
    width: 240px;
    display: flex;
    flex-shrink: 0;
    flex-direction: column;
    background: var(--secondary-background);
insert's avatar
insert committed

    border-start-start-radius: 8px;
insert's avatar
insert committed
`;

const ServerList = styled.div`
    padding: 6px;
    flex-grow: 1;
    overflow-y: scroll;

    > svg {
        width: 100%;
    }
`;

insert's avatar
insert committed
function ServerSidebar(props: Props & WithDispatcher) {
    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>[])
insert's avatar
insert committed
        .map(x => mapChannelWithUnread(x, props.unreads));
    
    const channel = channels.find(x => x?._id === channel_id);
insert's avatar
insert committed
    if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
insert's avatar
insert committed
    if (channel) useUnreads({ ...props, channel }, ctx);

insert's avatar
insert committed
    useEffect(() => {
        if (!channel_id) return;

        props.dispatcher({
            type: 'LAST_OPENED_SET',
            parent: server_id!,
            child: channel_id!
        });
    }, [ channel_id ]);

insert's avatar
insert committed
    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 active={active} to={`/server/${server!._id}/channel/${entry._id}`}>
                <ChannelButton
                    key={entry._id}
                    channel={entry}
                    active={active}
                    alert={entry.unread}
                    compact
                />
            </ConditionalLink>
        );
    }

    if (server.categories) {
        for (let category of server.categories) {
            elements.push(<Category text={category.title} />);

            for (let id of category.channels) {
                uncategorised.delete(id);
                elements.push(addChannel(id));
            }
        }
    }

    for (let id of uncategorised) {
        elements.unshift(addChannel(id));
    }

insert's avatar
insert committed
    return (
insert's avatar
insert committed
        <ServerBase>
insert's avatar
insert committed
            <ServerHeader server={server} ctx={ctx} />
insert's avatar
insert committed
            <ConnectionStatus />
insert's avatar
insert committed
            <ServerList onContextMenu={attachContextMenu('Menu', { server_list: server._id })}>
insert's avatar
insert committed
                { elements }
insert's avatar
insert committed
            </ServerList>
insert's avatar
insert committed
            <PaintCounter small />
insert's avatar
insert committed
        </ServerBase>
insert's avatar
insert committed
    )
};

export default connectState(
    ServerSidebar,
    state => {
        return {
            unreads: state.unreads
        };
    },
    true
);