From d028d83d37d26375916f32fc24c6be577e3e96a8 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Mon, 10 Jun 2024 17:23:11 -0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(frontend):=20add=20image=20preload?= =?UTF-8?q?ing=20hook=20and=20suspense=20image=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add `usePreloadImages` hook to preload profile pictures - Add `SuspenseImageComponent` for handling image loading with suspense - Update `ProfilePictureChooserComponent` to use the new hook and component --- .../hooks/use-preload-images.tsx | 42 ++++++++++++++++++ .../profilePictureChooserComponent/index.tsx | 20 ++++++--- .../suspenseImageComponent/index.tsx | 43 +++++++++++++++++++ 3 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx create mode 100644 src/frontend/src/shared/components/suspenseImageComponent/index.tsx diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx new file mode 100644 index 000000000..988af6ea9 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/hooks/use-preload-images.tsx @@ -0,0 +1,42 @@ +import { useEffect } from "react"; +import { + BACKEND_URL, + BASE_URL_API, +} from "../../../../../../../../../constants/constants"; + +const usePreloadImages = (profilePictures, setImagesLoaded) => { + const preloadImages = async (imageUrls) => { + return Promise.all( + imageUrls.map( + (src) => + new Promise((resolve) => { + const img = new Image(); + img.src = src; + img.onload = resolve; + img.onerror = resolve; + }), + ), + ); + }; + + useEffect(() => { + const imageArray: string[] = []; + const firstUrl = `${BACKEND_URL.slice(0, BACKEND_URL.length - 1)}`; + + Object.keys(profilePictures).flatMap((folder) => + profilePictures[folder].map((path) => + imageArray.push( + `${firstUrl}${BASE_URL_API}files/profile_pictures/${folder}/${path}`, + ), + ), + ); + + preloadImages(imageArray).then(() => { + setImagesLoaded(true); + }); + }, [profilePictures]); + + return; +}; + +export default usePreloadImages; diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx index 4e1544132..408bd8ee1 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent/index.tsx @@ -1,12 +1,13 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; +import { Button } from "../../../../../../../../components/ui/button"; +import Loading from "../../../../../../../../components/ui/loading"; import { BACKEND_URL, BASE_URL_API, } from "../../../../../../../../constants/constants"; -import Loading from "../../../../../../../../components/ui/loading"; -import { cn } from "../../../../../../../../utils/utils"; -import { Button } from "../../../../../../../../components/ui/button"; import { useDarkStore } from "../../../../../../../../stores/darkStore"; +import { cn } from "../../../../../../../../utils/utils"; +import usePreloadImages from "./hooks/use-preload-images"; type ProfilePictureChooserComponentProps = { profilePictures: { [key: string]: string[] }; @@ -23,6 +24,7 @@ export default function ProfilePictureChooserComponent({ }: ProfilePictureChooserComponentProps) { const ref = useRef(null); const dark = useDarkStore((state) => state.dark); + const [imagesLoaded, setImagesLoaded] = useState(false); useEffect(() => { if (value && ref) { @@ -30,9 +32,11 @@ export default function ProfilePictureChooserComponent({ } }, [ref, value]); + usePreloadImages(profilePictures, setImagesLoaded); + return (
- {loading ? ( + {loading || !imagesLoaded ? ( ) : ( Object.keys(profilePictures).map((folder, idx) => ( @@ -41,7 +45,7 @@ export default function ProfilePictureChooserComponent({ {folder}
-
+
{profilePictures[folder].map((path, idx) => (