Skip to content
Snippets Groups Projects
HomeSidebar.tsx 6.43 KiB
Newer Older
insert's avatar
insert committed
import { Localizer, Text } from "preact-i18n";
insert's avatar
insert committed
import { useContext, useEffect } from "preact/hooks";
import { Home, UserDetail, Wrench, Save } from "@styled-icons/boxicons-solid";
insert's avatar
insert committed

insert's avatar
insert committed
import Category from '../../ui/Category';
import PaintCounter from "../../../lib/PaintCounter";
import UserHeader from "../../common/user/UserHeader";
import { Channels } from "revolt.js/dist/api/objects";
import { connectState } from "../../../redux/connector";
import ConnectionStatus from '../items/ConnectionStatus';
insert's avatar
insert committed
import { WithDispatcher } from "../../../redux/reducers";
import { Unreads } from "../../../redux/reducers/unreads";
insert's avatar
insert committed
import ConditionalLink from "../../../lib/ConditionalLink";
insert's avatar
insert committed
import { mapChannelWithUnread, useUnreads } from "./common";
insert's avatar
insert committed
import { Users as UsersNS } from 'revolt.js/dist/api/objects';
insert's avatar
insert committed
import ButtonItem, { ChannelButton } from '../items/ButtonItem';
insert's avatar
insert committed
import { AppContext } from "../../../context/revoltjs/RevoltClient";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
insert's avatar
insert committed
import { Link, Redirect, useLocation, useParams } from "react-router-dom";
insert's avatar
insert committed
import { useIntermediate } from "../../../context/intermediate/Intermediate";
insert's avatar
insert committed
import { useDMs, useForceUpdate, useUsers } from "../../../context/revoltjs/hooks";
insert's avatar
insert committed

import placeholderSVG from "../items/placeholder.svg";

insert's avatar
insert committed
type Props = WithDispatcher & {
    unreads: Unreads;
}

function HomeSidebar(props: Props) {
    const { pathname } = useLocation();
insert's avatar
insert committed
    const client = useContext(AppContext);
insert's avatar
insert committed
    const { channel } = useParams<{ channel: string }>();
insert's avatar
insert committed
    const { openScreen } = useIntermediate();
insert's avatar
insert committed

    const ctx = useForceUpdate();
insert's avatar
insert committed
    const channels = useDMs(ctx);
insert's avatar
insert committed

    const obj = channels.find(x => x?._id === channel);
    if (channel && !obj) return <Redirect to="/" />;
    if (obj) useUnreads({ ...props, channel: obj });

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

        props.dispatcher({
            type: 'LAST_OPENED_SET',
            parent: 'home',
            child: channel
        });
    }, [ channel ]);

insert's avatar
insert committed
    const channelsArr = channels
        .filter(x => x.channel_type !== 'SavedMessages')
insert's avatar
insert committed
        .map(x => mapChannelWithUnread(x, props.unreads));

insert's avatar
insert committed
    const users = useUsers(
        (channelsArr as (Channels.DirectMessageChannel | Channels.GroupChannel)[])
            .reduce((prev: any, cur) => [ ...prev, ...cur.recipients ], [])
    , ctx);

insert's avatar
insert committed
    channelsArr.sort((b, a) => a.timestamp.localeCompare(b.timestamp));

    return (
insert's avatar
insert committed
        <GenericSidebarBase padding>
insert's avatar
insert committed
            <UserHeader user={client.user!} />
            <ConnectionStatus />
            <GenericSidebarList>
insert's avatar
insert committed
                {!isTouchscreenDevice && (
                    <>
insert's avatar
insert committed
                        <ConditionalLink active={pathname === "/"} to="/">
insert's avatar
insert committed
                            <ButtonItem active={pathname === "/"}>
                                <Home size={20} />
                                <span><Text id="app.navigation.tabs.home" /></span>
                            </ButtonItem>
insert's avatar
insert committed
                        </ConditionalLink>
                        <ConditionalLink active={pathname === "/friends"} to="/friends">
insert's avatar
insert committed
                            <ButtonItem
                                active={pathname === "/friends"}
                                alert={
                                    typeof users.find(
                                        user =>
                                            user?.relationship ===
                                            UsersNS.Relationship.Incoming
                                    ) !== "undefined" ? 'unread' : undefined
                                }
                            >
                                <UserDetail size={20} />
insert's avatar
insert committed
                                <span><Text id="app.navigation.tabs.friends" /></span>
                            </ButtonItem>
insert's avatar
insert committed
                        </ConditionalLink>
insert's avatar
insert committed
                    </>
                )}
insert's avatar
insert committed
                <ConditionalLink active={obj?.channel_type === "SavedMessages"} to="/open/saved">
insert's avatar
insert committed
                    <ButtonItem active={obj?.channel_type === "SavedMessages"}>
                        <Save size={20} />
                        <span><Text id="app.navigation.tabs.saved" /></span>
                    </ButtonItem>
insert's avatar
insert committed
                </ConditionalLink>
insert's avatar
insert committed
                {import.meta.env.DEV && (
                    <Link to="/dev">
                        <ButtonItem active={pathname === "/dev"}>
                            <Wrench size={20} />
insert's avatar
insert committed
                            <span><Text id="app.navigation.tabs.dev" /></span>
                        </ButtonItem>
                    </Link>
                )}
                <Localizer>
                    <Category
                        text={
                            (
                                <Text id="app.main.categories.conversations" />
                            ) as any
                        }
insert's avatar
insert committed
                        action={() => openScreen({ id: "special_input", type: "create_group" })}
insert's avatar
insert committed
                    />
                </Localizer>
                {channelsArr.length === 0 && <img src={placeholderSVG} />}
insert's avatar
insert committed
                {channelsArr.map(x => {
                    let user;
                    if (x.channel_type === 'DirectMessage') {
                        if (!x.active) return null;

insert's avatar
insert committed
                        let recipient = client.channels.getRecipient(x._id);
insert's avatar
insert committed
                        user = users.find(x => x?._id === recipient);
insert's avatar
insert committed
                        if (!user) {
                            console.warn(`Skipped DM ${x._id} because user was missing.`);
                            return null;
                        }
insert's avatar
insert committed
                    }
                    
                    return (
insert's avatar
insert committed
                        <ConditionalLink active={x._id === channel} to={`/channel/${x._id}`}>
insert's avatar
insert committed
                            <ChannelButton
                                user={user}
                                channel={x}
                                alert={x.unread}
                                alertCount={x.alertCount}
                                active={x._id === channel}
                            />
insert's avatar
insert committed
                        </ConditionalLink>
insert's avatar
insert committed
                    );
                })}
                <PaintCounter />
            </GenericSidebarList>
        </GenericSidebarBase>
insert's avatar
insert committed
    );
};

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