diff --git a/components/ImageBox/ImageBox.jsx b/components/ImageBox/ImageBox.jsx index 4c3381a..5cf4332 100644 --- a/components/ImageBox/ImageBox.jsx +++ b/components/ImageBox/ImageBox.jsx @@ -1,5 +1,5 @@ import Image from "next/image"; -import React from "react"; +import React, { useMemo } from "react"; import Styles from "./ImageBox.module.scss"; function ImageBox({ @@ -10,35 +10,47 @@ function ImageBox({ alt, src, style, - layout, + layout = "fill", aspectRatio, + priority = false, + quality = 75, + sizes, ...rest }) { - let dimensions = {}; - width ? (dimensions["width"] = width) : "100%"; - maxWidth ? (dimensions["maxWidth"] = maxWidth) : "100%"; - height ? (dimensions["height"] = height) : "unset"; - maxHeight ? (dimensions["maxHeight"] = maxHeight) : "unset"; + const dimensions = useMemo(() => { + const dims = {}; + if (width) dims.width = width; + if (maxWidth) dims.maxWidth = maxWidth; + if (height) dims.height = height; + if (maxHeight) dims.maxHeight = maxHeight; + return dims; + }, [width, maxWidth, height, maxHeight]); + + const containerClass = aspectRatio ? + Styles.imageRatioContainer : + Styles.imageContainer; + + const defaultSizes = sizes || "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"; return ( -
+
{alt
); } -export default ImageBox; +export default React.memo(ImageBox); diff --git a/components/QuestionBox/QuestionBox.jsx b/components/QuestionBox/QuestionBox.jsx index ebf9b5a..c414fbf 100644 --- a/components/QuestionBox/QuestionBox.jsx +++ b/components/QuestionBox/QuestionBox.jsx @@ -1,31 +1,44 @@ -import React, { useState } from "react"; +import React, { useState, useCallback, useMemo } from "react"; import Styles from "./QuestionBox.module.scss"; import { BiChevronRight, BiChevronDown } from "react-icons/bi"; function QuestionBox({ question }) { const [isOpen, setIsOpen] = useState(false); + + const toggleOpen = useCallback(() => { + setIsOpen(prev => !prev); + }, []); + + const sanitizedAnswer = useMemo(() => { + return question?.attributes?.answer.replace( + /)<[^<]*)*<\/script>/gi, + '' + ); + }, [question?.attributes?.answer]); + return (
-
{ - isOpen ? setIsOpen(false) : setIsOpen(true); - }} + onClick={toggleOpen} + aria-expanded={isOpen} + aria-controls={`answer-${question?.id}`} >

{question?.attributes?.question}

-
+ -
+ {isOpen && (
+ dangerouslySetInnerHTML={{ __html: sanitizedAnswer }} + /> )}
); } -export default QuestionBox; +export default React.memo(QuestionBox); diff --git a/components/StatBox/StatBox.jsx b/components/StatBox/StatBox.jsx index a6cd13b..37285d6 100644 --- a/components/StatBox/StatBox.jsx +++ b/components/StatBox/StatBox.jsx @@ -1,41 +1,31 @@ -import React, { useContext } from "react"; +import React, { useContext, useMemo } from "react"; import styles from "./StatBox.module.scss"; import ThemeContext from "../../utils/services/Themecontext"; import CountUp from "react-countup"; +const StatItem = React.memo(({ label, value, suffix = "+", decimals = 0 }) => ( +
+

{label}

+

+ +

+
+)); + function StatBox() { - const { theme, setTheme } = useContext(ThemeContext); + const { theme } = useContext(ThemeContext); + + const boxStyle = useMemo(() => ({ + ...(theme === "dark" ? { border: "1px solid #4f4f4f" } : {}) + }), [theme]); + return ( -
-
-

API Requests / Day

-

- -

-
-
-

Users

-

- -

-
-
-

Number of files

-

- -

-
+
+ + +
); } -export default StatBox; +export default React.memo(StatBox); diff --git a/components/metadata/Metadata.jsx b/components/metadata/Metadata.jsx index 69ed11e..ec91ac9 100644 --- a/components/metadata/Metadata.jsx +++ b/components/metadata/Metadata.jsx @@ -1,47 +1,57 @@ import Head from "next/head"; -import { useRouter } from "next/router"; -import React, { useEffect } from "react"; import Script from "next/script"; +import React from "react"; function Metadata({ title = "Lighthouse Storage - Store Data Permanently & Securely", description = "Decentralized storage powered by Filecoin. Secure, scalable, and ideal for individuals, developers, and enterprises.", url = "https://lighthouse.storage/", image = "https://gateway.lighthouse.storage/ipfs/Qmd7rR9EPKomhmoRUw2WB7FJAeSWAtC8c1nkKgGZL39LpB", + keywords = "decentralized storage, IPFS, Filecoin, permanent storage", }) { - const router = useRouter(); - return ( <> {title} + + + + + {/* Open Graph */} - {/* */} + {/* Twitter */} - + + + + - - ); } -export default Metadata; +export default React.memo(Metadata); diff --git a/containers/EcosystemGrid/EcosystemGrid.jsx b/containers/EcosystemGrid/EcosystemGrid.jsx index 5f45143..be64f69 100644 --- a/containers/EcosystemGrid/EcosystemGrid.jsx +++ b/containers/EcosystemGrid/EcosystemGrid.jsx @@ -1,10 +1,10 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useContext, useMemo, useCallback } from "react"; import styles from "./EcosystemGrid.module.scss"; import { RiTwitterXLine } from "react-icons/ri"; import { FaTelegramPlane } from "react-icons/fa"; import { SlGlobe } from "react-icons/sl"; import ThemeContext from "../../utils/services/Themecontext"; -import { ImageBox } from "../../components"; +import ImageBox from "../../components/ImageBox/ImageBox"; const badgeOptions = [ "Artificial Intelligence (AI)", @@ -391,10 +391,20 @@ const ecosystemData = [ const EcosystemGrid = () => { const [activeTag, setActiveTag] = useState("Artificial Intelligence (AI)"); - const filtered = ecosystemData.filter((item) => - item.tags.includes(activeTag) + const { theme } = useContext(ThemeContext); + + const filtered = useMemo(() => + ecosystemData.filter(item => item.tags.includes(activeTag)), + [activeTag] ); - const { theme, setTheme } = useContext(ThemeContext); + + const handleTagClick = useCallback((tag) => { + setActiveTag(tag); + }, []); + + const imageStyle = useMemo(() => ({ + filter: theme === "dark" ? "brightness(100%)" : "brightness(10%)" + }), [theme]); return (
@@ -402,10 +412,9 @@ const EcosystemGrid = () => { {badgeOptions.map((tag) => ( @@ -416,39 +425,24 @@ const EcosystemGrid = () => { {filtered .filter((item) => item.image) .map((item, idx) => ( -
+
{item.name}
- {/* {item.name} */} - - + -

{item.description}

{ ); }; -export default EcosystemGrid; +export default React.memo(EcosystemGrid); diff --git a/containers/index.js b/containers/index.js index eb3e590..2e17ba7 100644 --- a/containers/index.js +++ b/containers/index.js @@ -39,10 +39,16 @@ export const HoverContainer = dynamic(() => import("./HoverContainer/HoverContainer") ); export const LighthouseSuit = dynamic(() => - import("./LighthouseSuit/LighthouseSuit") + import("./LighthouseSuit/LighthouseSuit"), { + loading: () =>
Loading...
, + ssr: true + } ); export const Pricing = dynamic(() => - import("./Pricing/Pricing") + import("./Pricing/Pricing"), { + loading: () =>
Loading...
, + ssr: true + } ); export const EcosystemBanner = dynamic(() => import("./EcosystemBanner/EcosystemBanner") diff --git a/next.config.js b/next.config.js index a8fdd55..dab7c86 100644 --- a/next.config.js +++ b/next.config.js @@ -9,6 +9,23 @@ const nextConfig = { "gateway.lighthouse.storage", "cms.lighthouse.storage", ], + formats: ['image/avif', 'image/webp'], + minimumCacheTTL: 60, + }, + experimental: { + optimizeCss: true, + scrollRestoration: true, + }, + compiler: { + removeConsole: process.env.NODE_ENV === 'production', + }, + webpack: (config, { dev, isServer }) => { + config.optimization = { + ...config.optimization, + usedExports: true, + sideEffects: true, + } + return config }, }; diff --git a/package.json b/package.json index c5c94e0..ac611b1 100644 --- a/package.json +++ b/package.json @@ -6,27 +6,26 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "analyze": "ANALYZE=true next build" }, "dependencies": { "aos": "^2.3.4", "axios": "^1.1.3", - "lighthouse_ui_2.0": "file:", - "next": "12.3.1", + "next": "^12.3.1", "react": "18.2.0", "react-countup": "^6.5.0", "react-dom": "18.2.0", "react-icons": "^4.7.1", "react-markdown": "^8.0.5", "react-paginate": "^8.1.4", - "react-pagination": "^1.0.0", "react-toastify": "^9.1.1", "react-tooltip": "^5.28.0", - "rfs": "^10.0.0", "sass": "^1.55.0", "swiper": "^8.4.7" }, "devDependencies": { + "@next/bundle-analyzer": "^12.3.1", "eslint": "8.25.0", "eslint-config-next": "12.3.1" } diff --git a/pages/_app.js b/pages/_app.js index 03befd5..ce806f3 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -3,8 +3,6 @@ import "../styles/globals.scss"; import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; -import AOS from "aos"; -import "aos/dist/aos.css"; import { useEffect, useState } from "react"; import { ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; @@ -15,36 +13,45 @@ function MyApp({ Component, pageProps }) { const [theme, setTheme] = useState(null); useEffect(() => { - AOS.init({ - disable: false, - startEvent: "DOMContentLoaded", - initClassName: "aos-init", - animatedClassName: "aos-animate", - useClassNames: false, - disableMutationObserver: false, - debounceDelay: 50, - throttleDelay: 99, + const initAOS = async () => { + if (typeof window !== 'undefined') { + const AOS = (await import('aos')).default; + await import('aos/dist/aos.css'); + + AOS.init({ + disable: false, + startEvent: "DOMContentLoaded", + initClassName: "aos-init", + animatedClassName: "aos-animate", + useClassNames: false, + disableMutationObserver: false, + debounceDelay: 50, + throttleDelay: 99, + offset: 120, + delay: 0, + duration: 400, + easing: "ease", + once: true, + mirror: false, + anchorPlacement: "top-center", + }); + } + }; - offset: 120, - delay: 0, - duration: 400, - easing: "ease", - once: true, - mirror: false, - anchorPlacement: "top-center", - }); - const themeFromLocalStorage = JSON.parse( - localStorage?.getItem("lighthouse.storage/store") || "{}" - ); - if (themeFromLocalStorage?.theme) { - setTheme(themeFromLocalStorage?.theme); - } else { - setTheme("dark"); + initAOS(); + + if (typeof window !== 'undefined') { + const themeFromLocalStorage = JSON.parse( + localStorage?.getItem("lighthouse.storage/store") || "{}" + ); + setTheme(themeFromLocalStorage?.theme || "dark"); } }, []); useEffect(() => { - themeChanger(theme); + if (theme) { + themeChanger(theme); + } }, [theme]); return ( diff --git a/pages/blogs/index.js b/pages/blogs/index.js index 3bfdf9a..f381128 100644 --- a/pages/blogs/index.js +++ b/pages/blogs/index.js @@ -1,5 +1,5 @@ import axios from "axios"; -import React, { useEffect, useState } from "react"; +import React from "react"; import { Metadata } from "../../components"; import { FeaturedArticle, @@ -14,19 +14,30 @@ export const getStaticProps = async () => { try { const res = await axios.get(`${baseUrl}/blogs?pagination[pageSize]=50&populate=*`); blogsData = res["status"] === 200 ? res["data"]?.["data"] : null; - console.log(blogsData); - } catch (error) {} + } catch (error) { + console.error("Error fetching blogs:", error); + } + return { props: { - blogsData, + blogsData: blogsData || [], }, + revalidate: 3600, + notFound: !blogsData, }; }; function Blogs({ blogsData }) { + if (!blogsData) { + return
Loading...
; + } + return ( <> - +
diff --git a/utils/services/theme.js b/utils/services/theme.js index 36ee513..900781f 100644 --- a/utils/services/theme.js +++ b/utils/services/theme.js @@ -42,12 +42,24 @@ const ThemeProperties = [ ]; export const themeChanger = (theme) => { - theme && - localStorage.setItem("lighthouse.storage/store", JSON.stringify({ theme })); - ThemeProperties.forEach((property) => { - document.documentElement.style.setProperty( - `${property?.property}`, - theme === "dark" ? property?.dark : property?.light - ); - }); + if (!theme || typeof window === 'undefined') return; + + try { + requestAnimationFrame(() => { + localStorage.setItem( + "lighthouse.storage/store", + JSON.stringify({ theme }) + ); + + const style = document.documentElement.style; + ThemeProperties.forEach((property) => { + style.setProperty( + property.property, + theme === "dark" ? property.dark : property.light + ); + }); + }); + } catch (error) { + console.error('Error updating theme:', error); + } };