From 71c9cb96face87b3649ebc7d820b2f70f4bba7bf Mon Sep 17 00:00:00 2001
From: Michael Novotny <manovotny@gmail.com>
Date: Tue, 1 Aug 2023 20:18:56 -0500
Subject: [PATCH] Uses url instead of `setState` for image gallery (#1133)

---
 components/product/gallery.tsx | 60 +++++++++++++++++++---------------
 1 file changed, 34 insertions(+), 26 deletions(-)

diff --git a/components/product/gallery.tsx b/components/product/gallery.tsx
index 8636a1cc9..99742a917 100644
--- a/components/product/gallery.tsx
+++ b/components/product/gallery.tsx
@@ -2,33 +2,40 @@
 
 import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
 import { GridTileImage } from 'components/grid/tile';
+import { createUrl } from 'lib/utils';
 import Image from 'next/image';
-import { useState } from 'react';
+import Link from 'next/link';
+import { usePathname, useSearchParams } from 'next/navigation';
 
 export function Gallery({ images }: { images: { src: string; altText: string }[] }) {
-  const [currentImageIndex, setCurrentImageIndex] = useState(0);
+  const pathname = usePathname();
+  const searchParams = useSearchParams();
+  const imageSearchParam = searchParams.get('image');
+  const imageIndex = imageSearchParam ? parseInt(imageSearchParam) : 0;
 
-  function handleNavigate(direction: 'next' | 'previous') {
-    if (direction === 'next') {
-      setCurrentImageIndex(currentImageIndex + 1 < images.length ? currentImageIndex + 1 : 0);
-    } else {
-      setCurrentImageIndex(currentImageIndex === 0 ? images.length - 1 : currentImageIndex - 1);
-    }
-  }
+  const nextSearchParams = new URLSearchParams(searchParams.toString());
+  const nextImageIndex = imageIndex + 1 < images.length ? imageIndex + 1 : 0;
+  nextSearchParams.set('image', nextImageIndex.toString());
+  const nextUrl = createUrl(pathname, nextSearchParams);
+
+  const previousSearchParams = new URLSearchParams(searchParams.toString());
+  const previousImageIndex = imageIndex === 0 ? images.length - 1 : imageIndex - 1;
+  previousSearchParams.set('image', previousImageIndex.toString());
+  const previousUrl = createUrl(pathname, previousSearchParams);
 
   const buttonClassName =
-    'h-full px-6 transition-all ease-in-out hover:scale-110 hover:text-black dark:hover:text-white';
+    'h-full px-6 transition-all ease-in-out hover:scale-110 hover:text-black dark:hover:text-white flex items-center justify-center';
 
   return (
     <div className="mr-8 h-full">
       <div className="relative mb-12 h-full max-h-[550px] overflow-hidden">
-        {images[currentImageIndex] && (
+        {images[imageIndex] && (
           <Image
             className="relative h-full w-full object-contain"
             height={600}
             width={600}
-            alt={images[currentImageIndex]?.altText as string}
-            src={images[currentImageIndex]?.src as string}
+            alt={images[imageIndex]?.altText as string}
+            src={images[imageIndex]?.src as string}
             priority={true}
           />
         )}
@@ -36,21 +43,17 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
         {images.length > 1 ? (
           <div className="absolute bottom-[15%] flex w-full justify-center">
             <div className="mx-auto flex h-11 items-center rounded-full border border-white bg-neutral-50/80 text-neutral-500 backdrop-blur dark:border-black dark:bg-neutral-900/80">
-              <button
+              <Link
                 aria-label="Previous product image"
-                onClick={() => handleNavigate('previous')}
+                href={previousUrl}
                 className={buttonClassName}
               >
                 <ArrowLeftIcon className="h-5" />
-              </button>
+              </Link>
               <div className="mx-1 h-6 w-px bg-neutral-500"></div>
-              <button
-                aria-label="Next product image"
-                onClick={() => handleNavigate('next')}
-                className={buttonClassName}
-              >
+              <Link aria-label="Next product image" href={nextUrl} className={buttonClassName}>
                 <ArrowRightIcon className="h-5" />
-              </button>
+              </Link>
             </div>
           </div>
         ) : null}
@@ -59,13 +62,18 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
       {images.length > 1 ? (
         <div className="flex items-center justify-center gap-2 overflow-auto py-1">
           {images.map((image, index) => {
-            const isActive = index === currentImageIndex;
+            const isActive = index === imageIndex;
+            const imageSearchParams = new URLSearchParams(searchParams.toString());
+
+            imageSearchParams.set('image', index.toString());
+
             return (
-              <button
+              <Link
                 aria-label="Enlarge product image"
                 key={image.src}
                 className="h-auto w-20"
-                onClick={() => setCurrentImageIndex(index)}
+                href={createUrl(pathname, imageSearchParams)}
+                scroll={false}
               >
                 <GridTileImage
                   alt={image.altText}
@@ -74,7 +82,7 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
                   height={600}
                   active={isActive}
                 />
-              </button>
+              </Link>
             );
           })}
         </div>