ChatGPT-Next-Web/app/components/sidebar.tsx

147 lines
4.3 KiB
TypeScript
Raw Normal View History

2023-04-20 17:12:39 +00:00
import { useState, useEffect, useRef } from "react";
import styles from "./home.module.scss";
import { IconButton } from "./button";
import SettingsIcon from "../icons/settings.svg";
import GithubIcon from "../icons/github.svg";
import ChatGptIcon from "../icons/chatgpt.svg";
import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg";
import Locale from "../locales";
import { useChatStore } from "../store";
2023-04-20 18:52:53 +00:00
import {
MAX_SIDEBAR_WIDTH,
MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH,
Path,
REPO_URL,
} from "../constant";
2023-04-20 17:12:39 +00:00
import { HashRouter as Router, Link, useNavigate } from "react-router-dom";
import { useMobileScreen } from "../utils";
import { ChatList } from "./chat-list";
function useDragSideBar() {
2023-04-20 18:52:53 +00:00
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
2023-04-20 17:12:39 +00:00
const chatStore = useChatStore();
const startX = useRef(0);
const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300);
const lastUpdateTime = useRef(Date.now());
const handleMouseMove = useRef((e: MouseEvent) => {
2023-04-20 18:52:53 +00:00
if (Date.now() < lastUpdateTime.current + 50) {
2023-04-20 17:12:39 +00:00
return;
}
lastUpdateTime.current = Date.now();
const d = e.clientX - startX.current;
const nextWidth = limit(startDragWidth.current + d);
chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth));
});
const handleMouseUp = useRef(() => {
startDragWidth.current = chatStore.config.sidebarWidth ?? 300;
window.removeEventListener("mousemove", handleMouseMove.current);
window.removeEventListener("mouseup", handleMouseUp.current);
});
const onDragMouseDown = (e: MouseEvent) => {
startX.current = e.clientX;
window.addEventListener("mousemove", handleMouseMove.current);
window.addEventListener("mouseup", handleMouseUp.current);
};
const isMobileScreen = useMobileScreen();
2023-04-20 18:52:53 +00:00
const shouldNarrow =
!isMobileScreen && chatStore.config.sidebarWidth < MIN_SIDEBAR_WIDTH;
2023-04-20 17:12:39 +00:00
useEffect(() => {
2023-04-20 18:52:53 +00:00
const barWidth = shouldNarrow
? NARROW_SIDEBAR_WIDTH
: limit(chatStore.config.sidebarWidth ?? 300);
const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
2023-04-20 17:12:39 +00:00
document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
2023-04-20 18:52:53 +00:00
}, [chatStore.config.sidebarWidth, isMobileScreen, shouldNarrow]);
2023-04-20 17:12:39 +00:00
return {
onDragMouseDown,
2023-04-20 18:52:53 +00:00
shouldNarrow,
2023-04-20 17:12:39 +00:00
};
}
2023-04-20 18:52:53 +00:00
export function SideBar(props: { className?: string }) {
2023-04-20 17:12:39 +00:00
const chatStore = useChatStore();
// drag side bar
2023-04-20 18:52:53 +00:00
const { onDragMouseDown, shouldNarrow } = useDragSideBar();
2023-04-20 17:12:39 +00:00
const navigate = useNavigate();
return (
2023-04-20 18:52:53 +00:00
<div
className={`${styles.sidebar} ${props.className} ${
shouldNarrow && styles["narrow-sidebar"]
}`}
>
2023-04-20 17:12:39 +00:00
<div className={styles["sidebar-header"]}>
<div className={styles["sidebar-title"]}>ChatGPT Next</div>
<div className={styles["sidebar-sub-title"]}>
Build your own AI assistant.
</div>
<div className={styles["sidebar-logo"]}>
<ChatGptIcon />
</div>
</div>
<div
className={styles["sidebar-body"]}
onClick={(e) => {
if (e.target === e.currentTarget) {
navigate(Path.Home);
}
}}
>
2023-04-20 18:52:53 +00:00
<ChatList narrow={shouldNarrow} />
2023-04-20 17:12:39 +00:00
</div>
<div className={styles["sidebar-tail"]}>
<div className={styles["sidebar-actions"]}>
<div className={styles["sidebar-action"] + " " + styles.mobile}>
<IconButton
icon={<CloseIcon />}
onClick={chatStore.deleteSession}
/>
</div>
<div className={styles["sidebar-action"]}>
<Link to={Path.Settings}>
<IconButton icon={<SettingsIcon />} shadow />
</Link>
</div>
<div className={styles["sidebar-action"]}>
<a href={REPO_URL} target="_blank">
<IconButton icon={<GithubIcon />} shadow />
</a>
</div>
</div>
<div>
<IconButton
icon={<AddIcon />}
2023-04-20 18:52:53 +00:00
text={shouldNarrow ? undefined : Locale.Home.NewChat}
2023-04-20 17:12:39 +00:00
onClick={() => {
chatStore.newSession();
}}
shadow
/>
</div>
</div>
<div
className={styles["sidebar-drag"]}
onMouseDown={(e) => onDragMouseDown(e as any)}
></div>
2023-04-20 18:52:53 +00:00
</div>
2023-04-20 17:12:39 +00:00
);
}