perf: improve home and canvas initial load
This commit is contained in:
parent
67b526408a
commit
7c1d985c09
@ -1976,7 +1976,7 @@ export default function CanvasEditorLegacyPage({
|
||||
);
|
||||
const [activeWorkflowExecutionTaskKey, setActiveWorkflowExecutionTaskKey] =
|
||||
useState<string | null>(null);
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(() => !isVideoMode);
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(() => requestedAgentMode);
|
||||
const [floatingPanelWidth, setFloatingPanelWidth] =
|
||||
useState(FLOATING_PANEL_WIDTH);
|
||||
const [
|
||||
@ -3280,11 +3280,15 @@ export default function CanvasEditorLegacyPage({
|
||||
}, [checkCanvasAuthSession]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPanelOpen && !isVideoCanvasFileListOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
setAreSecondaryPanelsReady(true);
|
||||
}, 800);
|
||||
}, 120);
|
||||
return () => window.clearTimeout(timeoutId);
|
||||
}, []);
|
||||
}, [isPanelOpen, isVideoCanvasFileListOpen]);
|
||||
|
||||
const hydrateCanvasProject = useCallback(
|
||||
(project: CanvasInitialProject) => {
|
||||
@ -10844,7 +10848,7 @@ export default function CanvasEditorLegacyPage({
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex h-full w-full flex-col overflow-hidden border-l bg-white dark:bg-zinc-950">
|
||||
{!areSecondaryPanelsReady ? (
|
||||
{!isPanelOpen ? null : !areSecondaryPanelsReady ? (
|
||||
<CanvasLazyPanelLoader label="正在准备侧边面板..." />
|
||||
) : panelMode === "chat" ? (
|
||||
// AI 对话内容 - 使用 key 强制在 id 变化时重新挂载
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { signIn, signOut } from "next-auth/react";
|
||||
import { useTheme } from "next-themes";
|
||||
import useSWR from "swr";
|
||||
import useSWRInfinite from "swr/infinite";
|
||||
@ -60,7 +59,6 @@ import {
|
||||
getCanvasLaunchStorageKey,
|
||||
} from "@/lib/canvas-launch";
|
||||
import type { MembershipConfig } from "@/lib/membership/config";
|
||||
import { fetchUploadWithRetry } from "@/lib/client/upload-with-retry";
|
||||
import {
|
||||
normalizeLovartHomeSidebarConfig,
|
||||
type LovartHomeSidebarItemKey,
|
||||
@ -465,6 +463,7 @@ export function LovartHome({
|
||||
const [previewCursorVisible, setPreviewCursorVisible] = useState(true);
|
||||
const [isSecondaryDataReady, setIsSecondaryDataReady] = useState(false);
|
||||
const [isFeaturedDataReady, setIsFeaturedDataReady] = useState(false);
|
||||
const [isHomeAnimationReady, setIsHomeAnimationReady] = useState(false);
|
||||
const projectSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
const featuredSectionRef = useRef<HTMLElement | null>(null);
|
||||
const featuredLoadMoreRef = useRef<HTMLDivElement | null>(null);
|
||||
@ -535,6 +534,31 @@ export function LovartHome({
|
||||
};
|
||||
}, [isFeaturedDataReady]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined" || !isSecondaryDataReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadHomeAnimation = () => setIsHomeAnimationReady(true);
|
||||
const idleWindow = window as Window & {
|
||||
cancelIdleCallback?: (handle: number) => void;
|
||||
requestIdleCallback?: (
|
||||
callback: IdleRequestCallback,
|
||||
options?: IdleRequestOptions
|
||||
) => number;
|
||||
};
|
||||
|
||||
if (typeof idleWindow.requestIdleCallback === "function") {
|
||||
const idleId = idleWindow.requestIdleCallback(loadHomeAnimation, {
|
||||
timeout: 3200,
|
||||
});
|
||||
return () => idleWindow.cancelIdleCallback?.(idleId);
|
||||
}
|
||||
|
||||
const timer = window.setTimeout(loadHomeAnimation, 1800);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [isSecondaryDataReady]);
|
||||
|
||||
const { data: canvasData, mutate: mutateCanvas } = useSWR<{
|
||||
success: boolean;
|
||||
data: CanvasProject[];
|
||||
@ -671,7 +695,10 @@ export function LovartHome({
|
||||
enabled?: boolean;
|
||||
htmlContent?: string;
|
||||
fileName?: string;
|
||||
}>(isSecondaryDataReady ? "/api/home-animation" : null, fetcher);
|
||||
}>(
|
||||
isSecondaryDataReady && isHomeAnimationReady ? "/api/home-animation" : null,
|
||||
fetcher
|
||||
);
|
||||
|
||||
const membershipConfig = membershipData?.config;
|
||||
const membershipPlans = membershipConfig?.plans || [];
|
||||
@ -896,6 +923,7 @@ export function LovartHome({
|
||||
} catch (_error) {}
|
||||
|
||||
try {
|
||||
const { signOut } = await import("next-auth/react");
|
||||
await signOut({
|
||||
callbackUrl: "/",
|
||||
redirect: false,
|
||||
@ -1109,6 +1137,9 @@ export function LovartHome({
|
||||
};
|
||||
|
||||
const uploadSelectedFiles = async (): Promise<CanvasLaunchAttachment[]> => {
|
||||
const { fetchUploadWithRetry } = await import(
|
||||
"@/lib/client/upload-with-retry"
|
||||
);
|
||||
const result: CanvasLaunchAttachment[] = [];
|
||||
|
||||
for (const item of files) {
|
||||
@ -1146,6 +1177,7 @@ export function LovartHome({
|
||||
|
||||
setIsLoggingIn(true);
|
||||
try {
|
||||
const { signIn } = await import("next-auth/react");
|
||||
const result = await signIn("credentials", {
|
||||
email,
|
||||
password: loginPassword,
|
||||
@ -1171,6 +1203,7 @@ export function LovartHome({
|
||||
toast.error("Google 登录未启用");
|
||||
return;
|
||||
}
|
||||
const { signIn } = await import("next-auth/react");
|
||||
await signIn("google", { callbackUrl: "/" });
|
||||
};
|
||||
|
||||
@ -1226,6 +1259,9 @@ export function LovartHome({
|
||||
const blob = await response.blob();
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, `edited-${Date.now()}.png`);
|
||||
const { fetchUploadWithRetry } = await import(
|
||||
"@/lib/client/upload-with-retry"
|
||||
);
|
||||
const uploadResponse = await fetchUploadWithRetry(formData);
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
@ -1802,7 +1838,9 @@ export function LovartHome({
|
||||
heroPromoText={heroPromoText}
|
||||
heroSubtitleText={heroSubtitleText}
|
||||
heroTitleText={heroTitleText}
|
||||
homeAnimationData={homeAnimationData}
|
||||
homeAnimationData={
|
||||
isHomeAnimationReady ? homeAnimationData : undefined
|
||||
}
|
||||
isAgentModeActive={isComposerAgentModeActive}
|
||||
isMembershipEnabled={isMembershipEnabled}
|
||||
isSubmitting={isSubmitting}
|
||||
|
||||
@ -62,6 +62,8 @@ export function LovartFeaturedGallery({
|
||||
<img
|
||||
src={item.thumbnailUrl || item.originalUrl}
|
||||
alt={item.prompt || "灵感图"}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="block h-auto w-full rounded-[6px] bg-[#e8e8e8] object-cover transition-transform duration-300 ease-out will-change-transform group-hover:scale-[1.01] dark:bg-[#1b1c20]"
|
||||
/>
|
||||
<div className="mt-[8px] flex h-[22px] items-center justify-between text-[11px] font-normal leading-none text-[#b8b8b8] dark:text-white/45">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { ArrowUp, Box, Lightbulb, Loader2, Plus, Sparkles } from "lucide-react";
|
||||
import type { ChangeEvent } from "react";
|
||||
import { HomeAnimationEmbed } from "@/components/home/home-animation-embed";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
@ -12,6 +12,19 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ModelOption, UploadFileItem } from "./types";
|
||||
|
||||
const HomeAnimationEmbed = dynamic(
|
||||
() =>
|
||||
import("@/components/home/home-animation-embed").then(
|
||||
(module) => module.HomeAnimationEmbed
|
||||
),
|
||||
{
|
||||
loading: () => (
|
||||
<div className="h-[360px] w-full rounded-[24px] border border-black/10 bg-black/[0.03] md:h-[420px] dark:border-white/10 dark:bg-white/[0.04]" />
|
||||
),
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
type LovartHeroComposerProps = {
|
||||
composerPlaceholder: string;
|
||||
displayLogo: string;
|
||||
|
||||
@ -76,6 +76,8 @@ export function LovartProjectsSection({
|
||||
<img
|
||||
src={item.thumbnail}
|
||||
alt={item.title}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="h-full w-full object-cover transition-transform duration-300 ease-out will-change-transform group-hover:scale-[1.025]"
|
||||
/>
|
||||
) : (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user