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

245 lines
6.3 KiB
TypeScript
Raw Normal View History

2023-03-09 17:01:40 +00:00
"use client";
require("../polyfill");
2023-04-21 17:13:23 +00:00
import { useState, useEffect } from "react";
2023-03-11 17:14:07 +00:00
2023-03-12 19:06:21 +00:00
import styles from "./home.module.scss";
2023-03-09 17:01:40 +00:00
import BotIcon from "../icons/bot.svg";
import LoadingIcon from "../icons/three-dots.svg";
2023-03-10 18:25:33 +00:00
import { getCSSVar, useMobileScreen } from "../utils";
2023-03-13 16:25:07 +00:00
import dynamic from "next/dynamic";
import { Path, SlotID } from "../constant";
import { ErrorBoundary } from "./error";
import { getISOLang, getLang } from "../locales";
2023-04-20 17:12:39 +00:00
import {
HashRouter as Router,
Routes,
Route,
useLocation,
} from "react-router-dom";
import { SideBar } from "./sidebar";
import { useAppConfig } from "../store/config";
import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client";
import { type ClientApi, getClientApi } from "../client/api";
import { useAccessStore } from "../store";
2023-04-20 17:12:39 +00:00
2023-03-21 14:56:27 +00:00
export function Loading(props: { noLogo?: boolean }) {
return (
2023-04-20 17:12:39 +00:00
<div className={styles["loading-content"] + " no-dark"}>
2023-03-21 14:56:27 +00:00
{!props.noLogo && <BotIcon />}
<LoadingIcon />
</div>
);
}
2024-07-26 07:50:26 +00:00
const Artifacts = dynamic(async () => (await import("./artifacts")).Artifacts, {
loading: () => <Loading noLogo />,
});
const Settings = dynamic(async () => (await import("./settings")).Settings, {
loading: () => <Loading noLogo />,
});
const Chat = dynamic(async () => (await import("./chat")).Chat, {
loading: () => <Loading noLogo />,
});
const NewChat = dynamic(async () => (await import("./new-chat")).NewChat, {
loading: () => <Loading noLogo />,
});
const MaskPage = dynamic(async () => (await import("./mask")).MaskPage, {
loading: () => <Loading noLogo />,
});
2023-04-24 16:49:27 +00:00
2024-08-30 05:02:03 +00:00
const PluginPage = dynamic(async () => (await import("./plugin")).PluginPage, {
loading: () => <Loading noLogo />,
});
2024-08-14 14:28:05 +00:00
const SearchChat = dynamic(
async () => (await import("./search-chat")).SearchChatPage,
{
loading: () => <Loading noLogo />,
},
);
const Sd = dynamic(async () => (await import("./sd")).Sd, {
loading: () => <Loading noLogo />,
});
2023-04-20 18:52:53 +00:00
export function useSwitchTheme() {
2023-04-21 16:12:07 +00:00
const config = useAppConfig();
2023-03-12 19:06:21 +00:00
useEffect(() => {
document.body.classList.remove("light");
document.body.classList.remove("dark");
2023-03-12 19:06:21 +00:00
if (config.theme === "dark") {
document.body.classList.add("dark");
} else if (config.theme === "light") {
document.body.classList.add("light");
}
const metaDescriptionDark = document.querySelector(
2023-05-12 10:47:41 +00:00
'meta[name="theme-color"][media*="dark"]',
);
const metaDescriptionLight = document.querySelector(
2023-05-12 10:47:41 +00:00
'meta[name="theme-color"][media*="light"]',
);
2023-03-15 17:24:03 +00:00
if (config.theme === "auto") {
metaDescriptionDark?.setAttribute("content", "#151515");
metaDescriptionLight?.setAttribute("content", "#fafafa");
} else {
2023-05-12 10:47:41 +00:00
const themeColor = getCSSVar("--theme-color");
metaDescriptionDark?.setAttribute("content", themeColor);
metaDescriptionLight?.setAttribute("content", themeColor);
}
}, [config.theme]);
2023-03-19 15:13:10 +00:00
}
function useHtmlLang() {
useEffect(() => {
const lang = getISOLang();
const htmlLang = document.documentElement.lang;
if (lang !== htmlLang) {
document.documentElement.lang = lang;
}
}, []);
}
2023-03-27 08:58:53 +00:00
const useHasHydrated = () => {
const [hasHydrated, setHasHydrated] = useState<boolean>(false);
useEffect(() => {
setHasHydrated(true);
}, []);
return hasHydrated;
};
2023-05-14 15:25:22 +00:00
const loadAsyncGoogleFont = () => {
const linkEl = document.createElement("link");
2023-06-14 17:48:56 +00:00
const proxyFontUrl = "/google-fonts";
const remoteFontUrl = "https://fonts.googleapis.com";
const googleFontUrl =
getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl;
2023-05-14 15:25:22 +00:00
linkEl.rel = "stylesheet";
linkEl.href =
googleFontUrl +
"/css2?family=" +
encodeURIComponent("Noto Sans:wght@300;400;700;900") +
"&display=swap";
2023-05-14 15:25:22 +00:00
document.head.appendChild(linkEl);
};
2024-07-22 16:51:58 +00:00
export function WindowContent(props: { children: React.ReactNode }) {
return (
<div className={styles["window-content"]} id={SlotID.AppBody}>
{props?.children}
</div>
);
}
2023-04-23 17:15:44 +00:00
function Screen() {
2023-04-21 16:12:07 +00:00
const config = useAppConfig();
2023-04-20 17:12:39 +00:00
const location = useLocation();
2024-07-26 07:50:26 +00:00
const isArtifact = location.pathname.includes(Path.Artifacts);
2023-04-20 17:12:39 +00:00
const isHome = location.pathname === Path.Home;
2023-06-06 18:18:24 +00:00
const isAuth = location.pathname === Path.Auth;
2024-07-22 16:51:58 +00:00
const isSd = location.pathname === Path.Sd;
2024-07-23 12:24:56 +00:00
const isSdNew = location.pathname === Path.SdNew;
2024-07-22 16:51:58 +00:00
2023-04-23 17:15:44 +00:00
const isMobileScreen = useMobileScreen();
2023-12-23 18:15:30 +00:00
const shouldTightBorder =
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
2023-04-10 18:56:48 +00:00
2023-05-14 15:25:22 +00:00
useEffect(() => {
loadAsyncGoogleFont();
}, []);
if (isArtifact) {
return (
<Routes>
2024-07-26 07:50:26 +00:00
<Route path="/artifacts/:id" element={<Artifacts />} />
</Routes>
);
}
2024-07-22 16:51:58 +00:00
const renderContent = () => {
if (isAuth) return <AuthPage />;
if (isSd) return <Sd />;
2024-07-23 14:23:34 +00:00
if (isSdNew) return <Sd />;
2024-07-22 16:51:58 +00:00
return (
<>
<SideBar className={isHome ? styles["sidebar-show"] : ""} />
<WindowContent>
<Routes>
<Route path={Path.Home} element={<Chat />} />
<Route path={Path.NewChat} element={<NewChat />} />
<Route path={Path.Masks} element={<MaskPage />} />
2024-08-30 05:02:03 +00:00
<Route path={Path.Plugins} element={<PluginPage />} />
2024-08-14 14:28:05 +00:00
<Route path={Path.SearchChat} element={<SearchChat />} />
2024-07-22 16:51:58 +00:00
<Route path={Path.Chat} element={<Chat />} />
<Route path={Path.Settings} element={<Settings />} />
</Routes>
</WindowContent>
</>
);
};
2023-04-20 17:12:39 +00:00
return (
2023-04-23 17:15:44 +00:00
<div
2024-07-22 16:51:58 +00:00
className={`${styles.container} ${
shouldTightBorder ? styles["tight-container"] : styles.container
} ${getLang() === "ar" ? styles["rtl-screen"] : ""}`}
2023-04-23 17:15:44 +00:00
>
2024-07-22 16:51:58 +00:00
{renderContent()}
2023-04-10 18:56:48 +00:00
</div>
2023-03-09 17:01:40 +00:00
);
}
export function useLoadData() {
const config = useAppConfig();
2024-07-06 03:27:53 +00:00
const api: ClientApi = getClientApi(config.modelConfig.providerName);
useEffect(() => {
(async () => {
const models = await api.llm.models();
config.mergeModels(models);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}
export function Home() {
2023-04-20 18:52:53 +00:00
useSwitchTheme();
useLoadData();
useHtmlLang();
2023-04-20 17:12:39 +00:00
useEffect(() => {
console.log("[Config] got config from build time", getClientConfig());
2023-07-10 15:19:43 +00:00
useAccessStore.getState().fetch();
}, []);
2023-04-20 17:12:39 +00:00
if (!useHasHydrated()) {
return <Loading />;
}
return (
<ErrorBoundary>
2023-04-23 17:15:44 +00:00
<Router>
<Screen />
</Router>
</ErrorBoundary>
);
}