mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-09 03:26:38 +08:00
194 lines
5.1 KiB
TypeScript
194 lines
5.1 KiB
TypeScript
"use client";
|
||
|
||
require("../polyfill");
|
||
|
||
import { useState, useEffect } from "react";
|
||
|
||
import { IconButton } from "./button";
|
||
import styles from "./home.module.scss";
|
||
|
||
import SettingsIcon from "../icons/settings.svg";
|
||
import GithubIcon from "../icons/github.svg";
|
||
import ChatGptIcon from "../icons/chatgpt.svg";
|
||
|
||
import BotIcon from "../icons/bot.svg";
|
||
import AddIcon from "../icons/add.svg";
|
||
import LoadingIcon from "../icons/three-dots.svg";
|
||
import CloseIcon from "../icons/close.svg";
|
||
|
||
import { useChatStore } from "../store";
|
||
import { isMobileScreen } from "../utils";
|
||
import Locale from "../locales";
|
||
import { ChatList } from "./chat-list";
|
||
import { Chat } from "./chat";
|
||
|
||
import dynamic from "next/dynamic";
|
||
import { REPO_URL } from "../constant";
|
||
import { ErrorBoundary } from "./error";
|
||
|
||
export function Loading(props: { noLogo?: boolean }) {
|
||
return (
|
||
<div className={styles["loading-content"]}>
|
||
{!props.noLogo && <BotIcon />}
|
||
<LoadingIcon />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const Settings = dynamic(async () => (await import("./settings")).Settings, {
|
||
loading: () => <Loading noLogo />,
|
||
});
|
||
|
||
function useSwitchTheme() {
|
||
const config = useChatStore((state) => state.config);
|
||
|
||
useEffect(() => {
|
||
document.body.classList.remove("light");
|
||
document.body.classList.remove("dark");
|
||
|
||
if (config.theme === "dark") {
|
||
document.body.classList.add("dark");
|
||
} else if (config.theme === "light") {
|
||
document.body.classList.add("light");
|
||
}
|
||
|
||
const themeColor = getComputedStyle(document.body)
|
||
.getPropertyValue("--theme-color")
|
||
.trim();
|
||
const metaDescription = document.querySelector('meta[name="theme-color"]');
|
||
metaDescription?.setAttribute("content", themeColor);
|
||
}, [config.theme]);
|
||
}
|
||
|
||
const useHasHydrated = () => {
|
||
const [hasHydrated, setHasHydrated] = useState<boolean>(false);
|
||
|
||
useEffect(() => {
|
||
setHasHydrated(true);
|
||
}, []);
|
||
|
||
return hasHydrated;
|
||
};
|
||
|
||
function _Home() {
|
||
const [createNewSession, currentIndex, removeSession] = useChatStore(
|
||
(state) => [
|
||
state.newSession,
|
||
state.currentSessionIndex,
|
||
state.removeSession,
|
||
],
|
||
);
|
||
const loading = !useHasHydrated();
|
||
const [showSideBar, setShowSideBar] = useState(true);
|
||
|
||
// setting
|
||
const [openSettings, setOpenSettings] = useState(false);
|
||
const config = useChatStore((state) => state.config);
|
||
|
||
useSwitchTheme();
|
||
|
||
if (loading) {
|
||
return <Loading />;
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className={`${
|
||
config.tightBorder && !isMobileScreen()
|
||
? styles["tight-container"]
|
||
: styles.container
|
||
}`}
|
||
>
|
||
<div
|
||
className={styles.sidebar + ` ${showSideBar && styles["sidebar-show"]}`}
|
||
>
|
||
<div className={styles["sidebar-header"]}>
|
||
<div className={styles["sidebar-title"]}>ChatGPTScoPe</div>
|
||
<div className={styles["sidebar-sub-title"]}>
|
||
专注于ChatGPT,分享 ChatGPT 最新知识。你想要了解的,可能一个 ChatGPTScoPe 就足够了。
|
||
</div>
|
||
<div className={styles["sidebar-logo"]}>
|
||
<ChatGptIcon />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={styles["sidebar-body"]}
|
||
onClick={() => {
|
||
setOpenSettings(false);
|
||
setShowSideBar(false);
|
||
}}
|
||
>
|
||
<ChatList />
|
||
</div>
|
||
|
||
<div className={styles["sidebar-tail"]}>
|
||
<div className={styles["sidebar-actions"]}>
|
||
<div className={styles["sidebar-action"] + " " + styles.mobile}>
|
||
<IconButton
|
||
icon={<CloseIcon />}
|
||
onClick={() => {
|
||
if (confirm(Locale.Home.DeleteChat)) {
|
||
removeSession(currentIndex);
|
||
}
|
||
}}
|
||
/>
|
||
</div>
|
||
<div className={styles["sidebar-action"]}>
|
||
<IconButton
|
||
icon={<SettingsIcon />}
|
||
onClick={() => {
|
||
setOpenSettings(true);
|
||
setShowSideBar(false);
|
||
}}
|
||
shadow
|
||
/>
|
||
</div>
|
||
<div className={styles["sidebar-action"]}>
|
||
<a href={REPO_URL} target="_blank">
|
||
<IconButton icon={<GithubIcon />} shadow />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<IconButton
|
||
icon={<AddIcon />}
|
||
text={Locale.Home.NewChat}
|
||
onClick={() => {
|
||
createNewSession();
|
||
setShowSideBar(false);
|
||
}}
|
||
shadow
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className={styles["window-content"]}>
|
||
{openSettings ? (
|
||
<Settings
|
||
closeSettings={() => {
|
||
setOpenSettings(false);
|
||
setShowSideBar(true);
|
||
}}
|
||
/>
|
||
) : (
|
||
<Chat
|
||
key="chat"
|
||
showSideBar={() => setShowSideBar(true)}
|
||
sideBarShowing={showSideBar}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function Home() {
|
||
return (
|
||
<ErrorBoundary>
|
||
<_Home></_Home>
|
||
</ErrorBoundary>
|
||
);
|
||
}
|