"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"]}>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={() => { 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> ); }