From bac7d6f1964a1fe3d14545a7500e42447d6a8373 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 26 May 2026 19:49:45 +0800 Subject: [PATCH] perf: make lovart home shell load first --- app/(chat)/page.tsx | 51 ------------------------ app/api/site-settings/route.ts | 11 ++++++ app/layout.tsx | 56 ++++++++++---------------- app/page.tsx | 5 +++ components/home/lovart-home.tsx | 70 ++++++++++++++++++++++----------- 5 files changed, 84 insertions(+), 109 deletions(-) delete mode 100644 app/(chat)/page.tsx create mode 100644 app/page.tsx diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx deleted file mode 100644 index 3dc7a5e22..000000000 --- a/app/(chat)/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Suspense } from "react"; -import { LovartHome } from "@/components/home/lovart-home"; -import { auth } from "../(auth)/auth"; -import { getSiteSettings, getSystemSetting } from "@/lib/db/queries"; - -export default function Page() { - return ( - }> - - - ); -} - -async function NewChatPage() { - const [session, siteSettings, surpriseCategoriesSetting] = - await Promise.all([ - auth(), - getSiteSettings(), - getSystemSetting({ key: "home_surprise_categories" }).catch(() => undefined), - ]); - - const surpriseCategories = Array.isArray(surpriseCategoriesSetting?.value) - ? surpriseCategoriesSetting?.value - .map((item: unknown) => String(item || "").trim()) - .filter(Boolean) - : ["全部", "精选"]; - - return ( - - ); -} diff --git a/app/api/site-settings/route.ts b/app/api/site-settings/route.ts index cf92b3f2a..befa3d7f1 100644 --- a/app/api/site-settings/route.ts +++ b/app/api/site-settings/route.ts @@ -798,6 +798,7 @@ async function getScopedPublicSiteSettings( } const { settings, valueByKey } = await readScopedSiteSettings([ + HOME_SURPRISE_CATEGORIES_KEY, LOVART_COMPOSER_PREVIEW_TEXTS_KEY, LOVART_COMPOSER_PREFIX_KEY, LOVART_PROMPT_TAG_VISIBILITY_KEY, @@ -813,6 +814,16 @@ async function getScopedPublicSiteSettings( return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), + lovartPromoText: + typedSettings?.lovartPromoText || + "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠", + lovartPromoActionText: typedSettings?.lovartPromoActionText || "立即升级", + lovartHeroTitle: typedSettings?.lovartHeroTitle || "由壹点AI 让设计更简单", + lovartHeroSubtitle: + typedSettings?.lovartHeroSubtitle || "懂你的设计代理,帮你搞定一切", + surpriseCategories: normalizeSurpriseCategories( + valueByKey.get(HOME_SURPRISE_CATEGORIES_KEY) + ), canvasDefaultModelId: typedSettings?.canvasDefaultModelId || null, lovartComposerPrefix: normalizeLovartComposerPrefix( valueByKey.get(LOVART_COMPOSER_PREFIX_KEY) diff --git a/app/layout.tsx b/app/layout.tsx index 272befcfa..02a43736b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,36 +7,26 @@ import { ImageDeliveryOptimizer } from "@/components/image-delivery-optimizer"; import "./globals.css"; import { SessionProvider } from "next-auth/react"; -import { getSiteSettings } from "@/lib/db/queries"; +const metadataBase = process.env.AUTH_URL + ? new URL(process.env.AUTH_URL) + : undefined; -export async function generateMetadata(): Promise { - const settings = await getSiteSettings(); - const updatedAt = - "updatedAt" in settings && settings.updatedAt - ? new Date(settings.updatedAt).getTime() - : "default"; - const faviconUrl = `/api/favicon?v=${updatedAt}`; - const metadataBase = process.env.AUTH_URL - ? new URL(process.env.AUTH_URL) - : undefined; - - return { - metadataBase, - title: { - template: `%s | ${settings.siteName}`, - default: settings.siteName, - }, - description: settings.siteName, - icons: { - icon: [ - { url: faviconUrl, sizes: "any" }, - { url: faviconUrl, type: "image/x-icon" }, - ], - apple: faviconUrl, - shortcut: faviconUrl, - }, - }; -} +export const metadata: Metadata = { + metadataBase, + title: { + template: "%s | AI Chatbot", + default: "AI Chatbot", + }, + description: "AI Chatbot", + icons: { + icon: [ + { url: "/api/favicon", sizes: "any" }, + { url: "/api/favicon", type: "image/x-icon" }, + ], + apple: "/api/favicon", + shortcut: "/api/favicon", + }, +}; export const viewport = { maximumScale: 1, // Disable auto-zoom on mobile Safari @@ -153,15 +143,11 @@ const CHUNK_ERROR_RECOVERY_SCRIPT = `\ }); })();`; -export default async function RootLayout({ +export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - // 获取网站设置中的默认主题 - const settings = await getSiteSettings(); - const defaultTheme = settings.defaultTheme || "system"; - return ( diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 000000000..dde8f02c2 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,5 @@ +import { LovartHome } from "@/components/home/lovart-home"; + +export default function Page() { + return ; +} diff --git a/components/home/lovart-home.tsx b/components/home/lovart-home.tsx index fc778fef5..45672ae54 100644 --- a/components/home/lovart-home.tsx +++ b/components/home/lovart-home.tsx @@ -228,12 +228,12 @@ type AccountCenterResponse = { }; type LovartHomeProps = { - user: HomeUser | null; - isAuthenticated: boolean; - siteName: string; + user?: HomeUser | null; + isAuthenticated?: boolean; + siteName?: string; siteLogo?: string | null; siteLogoDark?: string | null; - surpriseCategories: string[]; + surpriseCategories?: string[]; heroPromoText?: string | null; heroPromoActionText?: string | null; heroTitleText?: string | null; @@ -422,27 +422,19 @@ function AccountMenuItem({ } export function LovartHome({ - user, - isAuthenticated, - siteName, - siteLogo, - siteLogoDark, - surpriseCategories, - heroPromoText, - heroPromoActionText, - heroTitleText, - heroSubtitleText, -}: LovartHomeProps) { + user = null, + isAuthenticated = false, + siteName: initialSiteName = "AI Chatbot", + siteLogo: initialSiteLogo = null, + siteLogoDark: initialSiteLogoDark = null, + surpriseCategories: initialSurpriseCategories = ["全部", "精选"], + heroPromoText: initialHeroPromoText = null, + heroPromoActionText: initialHeroPromoActionText = null, + heroTitleText: initialHeroTitleText = null, + heroSubtitleText: initialHeroSubtitleText = null, +}: LovartHomeProps = {}) { const router = useRouter(); const { theme, resolvedTheme, setTheme } = useTheme(); - const displayLogo = - resolvedTheme === "dark" - ? siteLogoDark?.trim() || siteLogo?.trim() || "" - : siteLogo?.trim() || siteLogoDark?.trim() || ""; - const shouldInvertLogo = - resolvedTheme === "dark" && - !siteLogoDark?.trim() && - Boolean(siteLogo?.trim()); const sidebarPopupBaseClass = "rounded-[24px] border border-black/8 bg-white/96 p-2.5 text-[#111827] shadow-[0_24px_64px_rgba(15,23,42,0.12)] backdrop-blur-xl"; const lovartSidebarButtonClass = (active = false) => @@ -578,6 +570,14 @@ export function LovartHome({ ); const { data: siteSettingsData } = useSWR<{ + siteName?: string | null; + siteLogo?: string | null; + siteLogoDark?: string | null; + lovartPromoText?: string | null; + lovartPromoActionText?: string | null; + lovartHeroTitle?: string | null; + lovartHeroSubtitle?: string | null; + surpriseCategories?: string[]; canvasDefaultModelId?: string | null; lovartComposerPrefix?: string | null; lovartComposerPreviewTexts?: string[]; @@ -603,6 +603,30 @@ export function LovartHome({ lovartHomeSidebarConfig?: unknown; }>("/api/site-settings?scope=home", fetcher); + const siteName = String( + siteSettingsData?.siteName || initialSiteName || "AI Chatbot" + ).trim(); + const siteLogo = String(siteSettingsData?.siteLogo || initialSiteLogo || "").trim(); + const siteLogoDark = String( + siteSettingsData?.siteLogoDark || initialSiteLogoDark || "" + ).trim(); + const heroPromoText = + siteSettingsData?.lovartPromoText ?? initialHeroPromoText; + const heroPromoActionText = + siteSettingsData?.lovartPromoActionText ?? initialHeroPromoActionText; + const heroTitleText = + siteSettingsData?.lovartHeroTitle ?? initialHeroTitleText; + const heroSubtitleText = + siteSettingsData?.lovartHeroSubtitle ?? initialHeroSubtitleText; + const surpriseCategories = + siteSettingsData?.surpriseCategories ?? initialSurpriseCategories; + const displayLogo = + resolvedTheme === "dark" + ? siteLogoDark || siteLogo || "" + : siteLogo || siteLogoDark || ""; + const shouldInvertLogo = + resolvedTheme === "dark" && !siteLogoDark && Boolean(siteLogo); + const { data: tutorialVideosData } = useSWR<{ videos: TutorialVideo[] }>( isSecondaryDataReady && siteSettingsData?.tutorialEnabled ? "/api/tutorial-videos"