feat: harden portfolio and add SEO foundations
This commit is contained in:
1 parent
b079afdd73
commit
62bcfbd8fa
121 files changed
+7769
-10469
No files matched your search
@@ -3,7 +3,7 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { PortfolioProject } from "@/lib/portfolio-data";
|
||||
import type { PortfolioProjectView } from "@/lib/portfolio-data";
|
||||
import NotebookFallback from "./NotebookFallback";
|
||||
import { selectNotebookProjects } from "./notebookProjects";
|
||||
import { getNotebookFrame } from "./notebookTimeline";
|
||||
@@ -18,7 +18,7 @@ const BROWSE_END = 0.78;
|
||||
type CapabilityWindow = Pick<Window, "innerWidth" | "matchMedia">;
|
||||
|
||||
export type NotebookStoryProps = {
|
||||
projects: PortfolioProject[];
|
||||
projects: PortfolioProjectView[];
|
||||
};
|
||||
|
||||
export function canUseNotebook3D(view: CapabilityWindow): boolean {
|
||||
@@ -64,11 +64,17 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
const progressRef = useRef(0);
|
||||
const activeIndexRef = useRef(notebookProjects.length > 0 ? 0 : -1);
|
||||
const [engine, setEngine] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [sectionIntersects, setSectionIntersects] = useState(false);
|
||||
const [isPageVisible, setIsPageVisible] = useState(
|
||||
() => typeof document === "undefined" || !document.hidden,
|
||||
);
|
||||
const [activeIndex, setActiveIndex] = useState(activeIndexRef.current);
|
||||
useEffect(() => {
|
||||
const updateEngine = () => {
|
||||
setEngine(notebookProjects.length > 0 && canUseNotebook3D(window));
|
||||
const nextEngine =
|
||||
notebookProjects.length > 0 && canUseNotebook3D(window);
|
||||
if (!nextEngine) setSectionIntersects(false);
|
||||
setEngine(nextEngine);
|
||||
};
|
||||
updateEngine();
|
||||
if (typeof window.matchMedia !== "function") return undefined;
|
||||
@@ -91,6 +97,15 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
};
|
||||
}, [notebookProjects.length]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleVisibilityChange = () => {
|
||||
setIsPageVisible(!document.hidden);
|
||||
};
|
||||
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||
return () =>
|
||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const section = sectionRef.current;
|
||||
if (!engine || !section || !("IntersectionObserver" in window)) {
|
||||
@@ -98,7 +113,7 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => setIsVisible(entry?.isIntersecting ?? false),
|
||||
([entry]) => setSectionIntersects(entry?.isIntersecting ?? false),
|
||||
{ threshold: 0 },
|
||||
);
|
||||
observer.observe(section);
|
||||
@@ -246,7 +261,7 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
</div>
|
||||
<NotebookStage3D
|
||||
activeIndex={activeIndex}
|
||||
isVisible={isVisible}
|
||||
isVisible={sectionIntersects && isPageVisible}
|
||||
onSelectTab={handleSelectTab}
|
||||
onStageError={handleStageError}
|
||||
progress={progressRef}
|
||||
|
||||
Reference in new issue
Block a user