chat-with-gpt/app/src/components/sidebar/index.tsx

159 lines
5.1 KiB
TypeScript

import styled from '@emotion/styled';
import { ActionIcon, Avatar, Burger, Button, Menu } from '@mantine/core';
import { useElementSize } from '@mantine/hooks';
import { useCallback, useMemo } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { backend } from '../../backend';
import { useAppContext } from '../../context';
import { useAppDispatch, useAppSelector } from '../../store';
import { setTab } from '../../store/settings-ui';
import { selectSidebarOpen, toggleSidebar } from '../../store/sidebar';
import RecentChats from './recent-chats';
const Container = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
position: relative;
font-family: "Work Sans", sans-serif;
box-shadow: 0px 0px 1rem 0.2rem rgb(0 0 0 / 5%);
.sidebar-header {
padding: 0.5rem 1rem 0.5rem 1.618rem;
min-height: 2.618rem;
display: flex;
align-items: center;
justify-content: space-between;
h2 {
font-size: 1rem;
font-weight: bold;
}
}
.sidebar-content {
flex-grow: 1;
overflow-y: scroll;
/* hide scrollbars */
&::-webkit-scrollbar {
display: none;
}
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
min-width: 20vw;
padding-bottom: 2rem;
}
.sidebar-footer {
border-top: thin solid rgba(255, 255, 255, 0.1);
padding: 0.5rem 1.118rem;
padding-left: 0.5rem;
flex-shrink: 0;
display: flex;
align-items: center;
font-size: 1rem;
cursor: pointer;
.user-info {
max-width: calc(100% - 1.618rem * 2 - 2.5rem);
margin-right: 0.5rem;
}
strong, span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
strong {
font-weight: bold;
margin-bottom: 0.2rem;
}
span {
font-size: 0.8rem;
font-weight: 100;
}
.mantine-Avatar-root {
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
overflow: hidden;
width: 2.5rem;
height: 2.5rem;
min-width: 0;
flex-grow: 0;
flex-shrink: 0;
margin: 0.5rem;
}
}
.spacer {
flex-grow: 1;
}
`;
export default function Sidebar(props: {
className?: string;
}) {
const intl = useIntl();
const dispatch = useAppDispatch();
const sidebarOpen = useAppSelector(selectSidebarOpen);
const onBurgerClick = useCallback(() => dispatch(toggleSidebar()), [dispatch]);
const { ref, width } = useElementSize();
const burgerLabel = sidebarOpen
? intl.formatMessage({ defaultMessage: "Close sidebar" })
: intl.formatMessage({ defaultMessage: "Open sidebar" });
const elem = useMemo(() => (
<Container className={"sidebar " + (sidebarOpen ? 'opened' : 'closed')} ref={ref}>
<div className="sidebar-header">
<h2><FormattedMessage defaultMessage={"Chat History"} /></h2>
<Burger opened={sidebarOpen} onClick={onBurgerClick} aria-label={burgerLabel} transitionDuration={0} />
</div>
<div className="sidebar-content">
<RecentChats />
</div>
{backend.current && backend.current.isAuthenticated && (
<Menu width={width - 20}>
<Menu.Target>
<div className="sidebar-footer">
<Avatar size="lg" src={backend.current!.user!.avatar} />
<div className="user-info">
<strong>{backend.current!.user!.name || backend.current!.user!.email}</strong>
{!!backend.current!.user!.name && <span>{backend.current.user!.email}</span>}
</div>
<div className="spacer" />
<ActionIcon variant="subtle">
<i className="fas fa-ellipsis" />
</ActionIcon>
</div>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={() => {
dispatch(setTab('user'));
}} icon={<i className="fas fa-gear" />}>
<FormattedMessage defaultMessage={"User settings"} />
</Menu.Item>
{/*
<Menu.Divider />
<Menu.Item color="red" onClick={() => backend.current?.logout()} icon={<i className="fas fa-sign-out-alt" />}>
<FormattedMessage defaultMessage={"Sign out"} />
</Menu.Item>
*/}
</Menu.Dropdown>
</Menu>
)}
</Container>
), [sidebarOpen, width, ref, burgerLabel, onBurgerClick, dispatch]);
return elem;
}