mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-10 20:16:37 +08:00
172 lines
4.9 KiB
TypeScript
172 lines
4.9 KiB
TypeScript
import { 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 { useAppConfig, useChatStore } from "../store";
|
||
|
||
import {
|
||
MAX_SIDEBAR_WIDTH,
|
||
MIN_SIDEBAR_WIDTH,
|
||
NARROW_SIDEBAR_WIDTH,
|
||
Path,
|
||
REPO_URL,
|
||
} from "../constant";
|
||
|
||
import { Link, useNavigate } from "react-router-dom";
|
||
import { useMobileScreen } from "../utils";
|
||
import dynamic from "next/dynamic";
|
||
|
||
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
|
||
loading: () => null,
|
||
});
|
||
|
||
function useDragSideBar() {
|
||
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
|
||
|
||
const config = useAppConfig();
|
||
const startX = useRef(0);
|
||
const startDragWidth = useRef(config.sidebarWidth ?? 300);
|
||
const lastUpdateTime = useRef(Date.now());
|
||
|
||
const handleMouseMove = useRef((e: MouseEvent) => {
|
||
if (Date.now() < lastUpdateTime.current + 50) {
|
||
return;
|
||
}
|
||
lastUpdateTime.current = Date.now();
|
||
const d = e.clientX - startX.current;
|
||
const nextWidth = limit(startDragWidth.current + d);
|
||
config.update((config) => (config.sidebarWidth = nextWidth));
|
||
});
|
||
|
||
const handleMouseUp = useRef(() => {
|
||
startDragWidth.current = 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();
|
||
const shouldNarrow =
|
||
!isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH;
|
||
|
||
useEffect(() => {
|
||
const barWidth = shouldNarrow
|
||
? NARROW_SIDEBAR_WIDTH
|
||
: limit(config.sidebarWidth ?? 300);
|
||
const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
|
||
document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
|
||
}, [config.sidebarWidth, isMobileScreen, shouldNarrow]);
|
||
|
||
return {
|
||
onDragMouseDown,
|
||
shouldNarrow,
|
||
};
|
||
}
|
||
|
||
export function SideBar(props: { className?: string }) {
|
||
const chatStore = useChatStore();
|
||
|
||
// drag side bar
|
||
const { onDragMouseDown, shouldNarrow } = useDragSideBar();
|
||
const navigate = useNavigate();
|
||
|
||
return (
|
||
<div
|
||
className={`${styles.sidebar} ${props.className} ${
|
||
shouldNarrow && styles["narrow-sidebar"]
|
||
}`}
|
||
>
|
||
<div className={styles["sidebar-header"]}>
|
||
|
||
<div className={styles["sidebar-title"]}>
|
||
<img src="logo.png" alt="smiley" />
|
||
</div>
|
||
|
||
<div className={styles["sidebar-sub-title"]}>
|
||
<p>
|
||
这里提供国内可访问的CHATGPT对话服务,希望你能好好利用资源,勿将此页面公开分享
|
||
</p>
|
||
<p>点击右下角《开始对话》直接使用</p>
|
||
<p>
|
||
This project is based on{" "}
|
||
<a
|
||
href="https://github.com/Yidadaa/ChatGPT-Next-Web"
|
||
target="_blank"
|
||
>
|
||
Yidadaa
|
||
</a>
|
||
,Thx.
|
||
</p>
|
||
<p>
|
||
<a
|
||
href="https://learningprompt.wiki/docs/chatgpt-learning-path"
|
||
target="_blank"
|
||
>
|
||
使用指南
|
||
</a>
|
||
</p>
|
||
</div>
|
||
<div className={styles["sidebar-logo"]}>
|
||
<ChatGptIcon />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={styles["sidebar-body"]}
|
||
onClick={(e) => {
|
||
if (e.target === e.currentTarget) {
|
||
navigate(Path.Home);
|
||
}
|
||
}}
|
||
>
|
||
<ChatList narrow={shouldNarrow} />
|
||
</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>
|
||
<div>
|
||
<IconButton
|
||
icon={<AddIcon />}
|
||
text={shouldNarrow ? undefined : Locale.Home.NewChat}
|
||
onClick={() => {
|
||
chatStore.newSession();
|
||
}}
|
||
shadow
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={styles["sidebar-drag"]}
|
||
onMouseDown={(e) => onDragMouseDown(e as any)}
|
||
></div>
|
||
</div>
|
||
);
|
||
}
|