2023-04-21 16:12:07 +00:00
|
|
|
import { useEffect, useRef } from "react";
|
2023-04-20 17:12:39 +00:00
|
|
|
|
|
|
|
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";
|
2023-04-26 18:12:09 +00:00
|
|
|
import MaskIcon from "../icons/mask.svg";
|
|
|
|
import PluginIcon from "../icons/plugin.svg";
|
|
|
|
|
2023-04-20 17:12:39 +00:00
|
|
|
import Locale from "../locales";
|
|
|
|
|
2023-04-21 16:12:07 +00:00
|
|
|
import { useAppConfig, useChatStore } from "../store";
|
2023-04-20 17:12:39 +00:00
|
|
|
|
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
|
|
|
|
2023-04-21 16:12:07 +00:00
|
|
|
import { Link, useNavigate } from "react-router-dom";
|
2023-04-20 17:12:39 +00:00
|
|
|
import { useMobileScreen } from "../utils";
|
2023-04-21 16:12:07 +00:00
|
|
|
import dynamic from "next/dynamic";
|
2023-04-26 18:12:09 +00:00
|
|
|
import { showToast } from "./ui-lib";
|
2023-04-21 16:12:07 +00:00
|
|
|
|
|
|
|
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
|
|
|
|
loading: () => null,
|
|
|
|
});
|
2023-04-20 17:12:39 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2023-04-21 16:12:07 +00:00
|
|
|
const config = useAppConfig();
|
2023-04-20 17:12:39 +00:00
|
|
|
const startX = useRef(0);
|
2023-04-21 16:12:07 +00:00
|
|
|
const startDragWidth = useRef(config.sidebarWidth ?? 300);
|
2023-04-20 17:12:39 +00:00
|
|
|
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);
|
2023-04-21 16:12:07 +00:00
|
|
|
config.update((config) => (config.sidebarWidth = nextWidth));
|
2023-04-20 17:12:39 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const handleMouseUp = useRef(() => {
|
2023-04-21 16:12:07 +00:00
|
|
|
startDragWidth.current = config.sidebarWidth ?? 300;
|
2023-04-20 17:12:39 +00:00
|
|
|
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 =
|
2023-04-21 16:12:07 +00:00
|
|
|
!isMobileScreen && 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
|
2023-04-21 16:12:07 +00:00
|
|
|
: limit(config.sidebarWidth ?? 300);
|
2023-04-20 18:52:53 +00:00
|
|
|
const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
|
2023-04-20 17:12:39 +00:00
|
|
|
document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
|
2023-04-21 16:12:07 +00:00
|
|
|
}, [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();
|
|
|
|
|
2023-04-27 16:34:37 +00:00
|
|
|
const config = useAppConfig();
|
|
|
|
|
2023-04-20 17:12:39 +00:00
|
|
|
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>
|
2023-04-21 16:35:50 +00:00
|
|
|
<div className={styles["sidebar-logo"] + " no-dark"}>
|
2023-04-20 17:12:39 +00:00
|
|
|
<ChatGptIcon />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2023-04-26 18:12:09 +00:00
|
|
|
<div className={styles["sidebar-header-bar"]}>
|
|
|
|
<IconButton
|
|
|
|
icon={<MaskIcon />}
|
2023-04-27 16:34:37 +00:00
|
|
|
text={shouldNarrow ? undefined : Locale.Mask.Name}
|
2023-04-26 18:12:09 +00:00
|
|
|
className={styles["sidebar-bar-button"]}
|
2023-04-27 16:34:37 +00:00
|
|
|
onClick={() => navigate(Path.NewChat, { state: { fromHome: true } })}
|
2023-04-26 18:12:09 +00:00
|
|
|
shadow
|
|
|
|
/>
|
|
|
|
<IconButton
|
|
|
|
icon={<PluginIcon />}
|
2023-04-27 16:34:37 +00:00
|
|
|
text={shouldNarrow ? undefined : Locale.Plugin.Name}
|
2023-04-26 18:12:09 +00:00
|
|
|
className={styles["sidebar-bar-button"]}
|
|
|
|
onClick={() => showToast(Locale.WIP)}
|
|
|
|
shadow
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
2023-04-20 17:12:39 +00:00
|
|
|
<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 />}
|
2023-05-01 15:21:28 +00:00
|
|
|
onClick={() => {
|
|
|
|
if (confirm(Locale.Home.DeleteChat)) {
|
|
|
|
chatStore.deleteSession(chatStore.currentSessionIndex);
|
|
|
|
}
|
|
|
|
}}
|
2023-04-20 17:12:39 +00:00
|
|
|
/>
|
|
|
|
</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={() => {
|
2023-04-27 16:34:37 +00:00
|
|
|
if (config.dontShowMaskSplashScreen) {
|
|
|
|
chatStore.newSession();
|
2023-05-03 15:49:33 +00:00
|
|
|
navigate(Path.Chat);
|
2023-04-27 16:34:37 +00:00
|
|
|
} else {
|
|
|
|
navigate(Path.NewChat);
|
|
|
|
}
|
2023-04-20 17:12:39 +00:00
|
|
|
}}
|
|
|
|
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
|
|
|
);
|
|
|
|
}
|