From cde5752219d57029a7ae857e9b23a851b0d284c8 Mon Sep 17 00:00:00 2001
From: eviterin <ethan.viterin@gmail.com>
Date: Sat, 16 Mar 2024 16:11:04 +0100
Subject: [PATCH] Readded code that was lost due to poor rebasing. Applied
 formatting fixes and ran eslint.

---
 packages/contracts/src/Inventory.sol          |   2 +-
 packages/webapp/src/actions/getDeck.ts        | 278 +++++++++--------
 packages/webapp/src/actions/setDeck.ts        | 191 ++++++------
 .../src/components/collection/deckList.tsx    | 112 ++++---
 .../src/components/collection/deckPanel.tsx   |  13 +-
 packages/webapp/src/pages/collection.tsx      | 290 +++++++++---------
 6 files changed, 481 insertions(+), 405 deletions(-)

diff --git a/packages/contracts/src/Inventory.sol b/packages/contracts/src/Inventory.sol
index 8225a42f..379bc239 100644
--- a/packages/contracts/src/Inventory.sol
+++ b/packages/contracts/src/Inventory.sol
@@ -339,7 +339,7 @@ contract Inventory is Ownable {
     {
         return decks[player][deckID].cards;
     }
-    
+
     // ---------------------------------------------------------------------------------------------
 
     // Returns the decks of a given player.
diff --git a/packages/webapp/src/actions/getDeck.ts b/packages/webapp/src/actions/getDeck.ts
index 2f396879..0162501f 100644
--- a/packages/webapp/src/actions/getDeck.ts
+++ b/packages/webapp/src/actions/getDeck.ts
@@ -1,120 +1,158 @@
-import { defaultErrorHandling } from "src/actions/errors"
-import { contractWriteThrowing } from "src/actions/libContractWrite"
-import { Address } from "src/chain"
-import { deployment } from "src/deployment"
-import { inventoryABI } from "src/generated"
-
-// =================================================================================================
-
-export type getAllDecksArgs = {
-  playerAddress: Address
-  onSuccess: () => void
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/**
- * Fetches all decks of the given player by sending the `getAllDecks` transaction.
- *
- * Returns `true` iff the transaction is successful.
- */
-export async function getAllDecks(args: getAllDecksArgs): Promise<any> {
-  try {
-    return await getAllDecksImpl(args)
-  } catch (err) {
-    defaultErrorHandling("getAllDecks", err)
-    return false
-  }
-}
-
-/**
- * Fetches the deck of the given player of a given ID by sending the `getDeck` transaction.
- *
- * Returns `true` iff the transaction is successful.
- */
-export async function getDeck(args: GetDeckAtArgs): Promise<any> {
-  try {
-    return await getDeckImpl(args)
-  } catch (err) {
-    defaultErrorHandling("getDeck", err)
-    return false
-  }
-}
-
-// -------------------------------------------------------------------------------------------------
-
-async function getAllDecksImpl(args: getAllDecksArgs): Promise<any> {
-    try {
-      const result = await contractWriteThrowing({
-        contract: deployment.Inventory,
-        abi: inventoryABI,
-        functionName: "getAllDecks",
-        args: [args.playerAddress],
-      }) 
-
-      args.onSuccess() 
-      return result 
-    } catch (error) {
-      console.error("Error fetching decks:", error)
-      return null
-    }
-  }
-
-// -------------------------------------------------------------------------------------------------
-
-async function getDeckImpl(args: GetDeckAtArgs): Promise<any> {
-  try {
-    const result = await contractWriteThrowing({
-      contract: deployment.Inventory,
-      abi: inventoryABI,
-      functionName: "getDeck",
-      args: [args.playerAddress, args.index],
-    }) 
-
-    args.onSuccess() 
-    return result 
-  } catch (error) {
-    console.error("Error fetching deck:", error)
-    return null
-  }
-}
-
-// -------------------------------------------------------------------------------------------------
-
-async function getNumDecksImpl(args: GetDeckArgs): Promise<any> {
-  try {
-    const result = await contractWriteThrowing({
-      contract: deployment.Inventory,
-      abi: inventoryABI,
-      functionName: "getNumDecks",
-      args: [args.playerAddress],
-    }) 
-
-    args.onSuccess() 
-    return result 
-  } catch (error) {
-    console.error("Error fetching decks:", error)
-    return null
-  }
-}
-
-// -------------------------------------------------------------------------------------------------
-
-async function getDeckNamesImpl(args: GetDeckArgs): Promise<any> {
-  try {
-    const result = await contractWriteThrowing({
-      contract: deployment.Inventory,
-      abi: inventoryABI,
-      functionName: "getDeckNames",
-      args: [args.playerAddress],
-    }) 
-
-    args.onSuccess() 
-    return result 
-  } catch (error) {
-    console.error("Error fetching decks:", error)
-    return null
-  }
-}
-
-// =================================================================================================
\ No newline at end of file
+import { defaultErrorHandling } from "src/actions/errors"
+import { contractWriteThrowing } from "src/actions/libContractWrite"
+import { Address } from "src/chain"
+import { deployment } from "src/deployment"
+import { inventoryABI } from "src/generated"
+
+// =================================================================================================
+
+export type GetDeckArgs = {
+    playerAddress: Address
+    onSuccess: () => void
+}
+
+export type GetDeckAtArgs = {
+    playerAddress: Address
+    onSuccess: () => void
+    index: number
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/**
+ * Fetches all decks of the given player by sending the `getAllDecks` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function getAllDecks(args: GetDeckArgs): Promise<any> {
+    try {
+        return await getAllDecksImpl(args)
+    } catch (err) {
+        defaultErrorHandling("getAllDecks", err)
+        return false
+    }
+}
+
+/**
+ * Fetches the deck of the given player of a given ID by sending the `getDeckReal` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function getDeck(args: GetDeckAtArgs): Promise<any> {
+    try {
+        return await getDeckImpl(args)
+    } catch (err) {
+        defaultErrorHandling("getDeck", err)
+        return false
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/**
+ * Fetches deck count of the given player by sending the `getNumDecks` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function getNumDecks(args: GetDeckArgs): Promise<any> {
+    try {
+        return await getNumDecksImpl(args)
+    } catch (err) {
+        defaultErrorHandling("getNumDecks", err)
+        return false
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/**
+ * Fetches deck count of the given player by sending the `getNumDecks` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function getDeckNames(args: GetDeckArgs): Promise<any> {
+    try {
+        return await getDeckNamesImpl(args)
+    } catch (err) {
+        defaultErrorHandling("getDeckNames", err)
+        return false
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+async function getAllDecksImpl(args: GetDeckArgs): Promise<any> {
+    try {
+        const result = await contractWriteThrowing({
+            contract: deployment.Inventory,
+            abi: inventoryABI,
+            functionName: "getAllDecks",
+            args: [args.playerAddress],
+        })
+
+        args.onSuccess()
+        return result
+    } catch (error) {
+        console.error("Error fetching decks:", error)
+        return null
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+async function getDeckImpl(args: GetDeckAtArgs): Promise<any> {
+    try {
+        const result = await contractWriteThrowing({
+            contract: deployment.Inventory,
+            abi: inventoryABI,
+            functionName: "getDeckReal",
+            args: [args.playerAddress, args.index],
+        })
+
+        args.onSuccess()
+        return result
+    } catch (error) {
+        console.error("Error fetching deck:", error)
+        return null
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+async function getNumDecksImpl(args: GetDeckArgs): Promise<any> {
+    try {
+        const result = await contractWriteThrowing({
+            contract: deployment.Inventory,
+            abi: inventoryABI,
+            functionName: "getNumDecks",
+            args: [args.playerAddress],
+        })
+
+        args.onSuccess()
+        return result
+    } catch (error) {
+        console.error("Error fetching decks:", error)
+        return null
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+async function getDeckNamesImpl(args: GetDeckArgs): Promise<any> {
+    try {
+        const result = await contractWriteThrowing({
+            contract: deployment.Inventory,
+            abi: inventoryABI,
+            functionName: "getDeckNames",
+            args: [args.playerAddress],
+        })
+
+        args.onSuccess()
+        return result
+    } catch (error) {
+        console.error("Error fetching decks:", error)
+        return null
+    }
+}
+
+// =================================================================================================
diff --git a/packages/webapp/src/actions/setDeck.ts b/packages/webapp/src/actions/setDeck.ts
index 4e20d60c..c114aeae 100644
--- a/packages/webapp/src/actions/setDeck.ts
+++ b/packages/webapp/src/actions/setDeck.ts
@@ -1,101 +1,90 @@
-import { defaultErrorHandling } from "src/actions/errors"
-import { contractWriteThrowing } from "src/actions/libContractWrite"
-import { Address } from "src/chain"
-import { deployment } from "src/deployment"
-import { inventoryABI } from "src/generated"
-import { checkFresh, freshWrap } from "src/store/checkFresh"
-import { Deck } from "src/store/types"
-
-// =================================================================================================
-
-export type SaveArgs = {
-  deck: Deck
-  playerAddress: Address
-  onSuccess: () => void
-}
-
-export type ModifyArgs = {
-  deck: Deck
-  playerAddress: Address
-  index: number
-  onSuccess: () => void
-}
-  
-
-// -------------------------------------------------------------------------------------------------
-
-/**
- * Saves a deck created by the player by sending the `saveDeck` transaction.
- *
- * Returns `true` iff the transaction is successful.
- */
-export async function save(args: SaveArgs): Promise<boolean> {
-  try {
-    return await saveImpl(args)
-  } catch (err) {
-    return defaultErrorHandling("save", err)
-  }
-}
-
-/**
- * Modifies a deck owned by the player by sending the `modifyDeck` transaction.
- *
- * Returns `true` iff the transaction is successful.
- */
-export async function modify(args: ModifyArgs): Promise<boolean> {
-  try {
-    return await modifyImpl(args)
-  } catch (err) {
-    return defaultErrorHandling("modify", err)
-  }
-}
-
-// -------------------------------------------------------------------------------------------------
-
-async function saveImpl(args: SaveArgs): Promise<boolean> {
-  const cardBigInts = args.deck.cards.map(card => card.id)
-
-  checkFresh(await freshWrap(
-    contractWriteThrowing({
-      contract: deployment.Inventory,
-      abi: inventoryABI,
-      functionName: "addDeck",
-      args: [
-        args.playerAddress,
-        { name: args.deck.name, cards: cardBigInts }
-      ],
-    })))
-
-  args.onSuccess()
-  return true
-}
-
-async function modifyImpl(args: ModifyArgs): Promise<boolean> {
-<<<<<<< HEAD
-<<<<<<< HEAD
-=======
->>>>>>> 35b5d7c (Modify deck support)
-    const cardBigInts = args.deck.cards.map(card => card.id)
-    console.log("INDEX: " + args.index)
-    checkFresh(await freshWrap(
-        contractWriteThrowing({
-          contract: deployment.Inventory,
-          abi: inventoryABI,
-          functionName: "replaceDeck",
-          args: [
-            args.playerAddress,
-            args.index,
-            { name: args.deck.name, cards: cardBigInts }
-          ],
-        })))
-  
-    args.onSuccess()
-    return true
-<<<<<<< HEAD
-=======
->>>>>>> 3d86518 (Can now save deck onchain)
-=======
->>>>>>> 35b5d7c (Modify deck support)
-}
-
-// =================================================================================================
\ No newline at end of file
+import { defaultErrorHandling } from "src/actions/errors"
+import { contractWriteThrowing } from "src/actions/libContractWrite"
+import { Address } from "src/chain"
+import { deployment } from "src/deployment"
+import { inventoryABI } from "src/generated"
+import { checkFresh, freshWrap } from "src/store/checkFresh"
+import { Deck } from "src/store/types"
+
+// =================================================================================================
+
+export type SaveArgs = {
+    deck: Deck
+    playerAddress: Address
+    onSuccess: () => void
+}
+
+export type ModifyArgs = {
+    deck: Deck
+    playerAddress: Address
+    index: number
+    onSuccess: () => void
+}
+
+// -------------------------------------------------------------------------------------------------
+
+/**
+ * Saves a deck created by the player by sending the `saveDeck` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function save(args: SaveArgs): Promise<boolean> {
+    try {
+        return await saveImpl(args)
+    } catch (err) {
+        return defaultErrorHandling("save", err)
+    }
+}
+
+/**
+ * Modifies a deck owned by the player by sending the `modifyDeck` transaction.
+ *
+ * Returns `true` iff the transaction is successful.
+ */
+export async function modify(args: ModifyArgs): Promise<boolean> {
+    try {
+        return await modifyImpl(args)
+    } catch (err) {
+        return defaultErrorHandling("modify", err)
+    }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+async function saveImpl(args: SaveArgs): Promise<boolean> {
+    const cardBigInts = args.deck.cards.map((card) => card.id)
+
+    checkFresh(
+        await freshWrap(
+            contractWriteThrowing({
+                contract: deployment.Inventory,
+                abi: inventoryABI,
+                functionName: "addDeck",
+                args: [args.playerAddress, { name: args.deck.name, cards: cardBigInts }],
+            })
+        )
+    )
+
+    args.onSuccess()
+    return true
+}
+
+async function modifyImpl(args: ModifyArgs): Promise<boolean> {
+    const cardBigInts = args.deck.cards.map((card) => card.id)
+    console.log("INDEX: " + args.index)
+    checkFresh(
+        await freshWrap(
+            contractWriteThrowing({
+                contract: deployment.Inventory,
+                abi: inventoryABI,
+                functionName: "replaceDeck",
+                args: [args.playerAddress, args.index, { name: args.deck.name, cards: cardBigInts }],
+            })
+        )
+    )
+
+    args.onSuccess()
+    return true
+}
+
+// =================================================================================================
diff --git a/packages/webapp/src/components/collection/deckList.tsx b/packages/webapp/src/components/collection/deckList.tsx
index 7e4ffd8e..770ecf34 100644
--- a/packages/webapp/src/components/collection/deckList.tsx
+++ b/packages/webapp/src/components/collection/deckList.tsx
@@ -1,47 +1,79 @@
-import React from 'react'
+import React, { useCallback, useEffect, useState } from "react"
+
+import { getDeckNames } from "src/actions/getDeck"
 import Link from "src/components/link"
-import { Deck } from 'src/store/types'
+import { Button } from "src/components/ui/button"
+import * as store from "src/store/hooks"
 
 interface DeckCollectionDisplayProps {
-  decks: Deck[]
-  onDeckSelect: (deckID: number) => void
-  isLoadingDecks: boolean
+    onDeckSelect: (deckID: number) => void
 }
 
-const DeckCollectionDisplay: React.FC<DeckCollectionDisplayProps> = ({ decks, onDeckSelect, isLoadingDecks }) => {
-  return (
-      <div className="w-full flex flex-col items-center p-3">
-        {/* New Deck Button */}
-        <Link 
-          href={"/collection?newDeck=true"} 
-          className="w-full px-4 py-2 mb-2 border rounded-md text-gray-100 bg-purple-900 hover:bg-gray-500 font-bold text-center focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
-          New Deck →
-        </Link>
-
-        {/* Loading Decks Button */}
-        {isLoadingDecks && (
-          <button
-            className="w-full px-4 py-2 mb-2 border rounded-md text-gray-100 bg-purple-500 font-bold text-center cursor-not-allowed"
-            disabled
-          >
-            Loading Decks...
-          </button>
-        )}
-
-        {/* Deck Buttons */}
-        {decks.map((deck, deckID) => (
-          <button 
-            key={deckID} 
-            onClick={() => onDeckSelect(deckID)}
-            className={`w-full px-4 py-2 mb-2 border rounded-md text-gray-100 ${isLoadingDecks ? 'bg-gray-400 cursor-not-allowed' : 'bg-purple-500 hover:bg-gray-500'} font-bold text-center focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent`}
-            disabled={isLoadingDecks}
-          >
-            {deck.name}
-          </button>
-        ))}
-
-      </div>
-  )
+const DeckCollectionDisplay: React.FC<DeckCollectionDisplayProps> = ({ onDeckSelect }) => {
+    const playerAddress = store.usePlayerAddress()
+    const [deckNames, setDeckNames] = useState<string[]>([])
+    const [isLoadingDecks, setIsLoadingDecks] = useState(false)
+
+    const loadDeckNames = useCallback(() => {
+        if (playerAddress) {
+            setIsLoadingDecks(true)
+            getDeckNames({
+                playerAddress: playerAddress,
+                onSuccess: () => {},
+            })
+                .then((response) => {
+                    if (!response.simulatedResult) return
+                    const receivedDecks = response.simulatedResult as string[]
+                    setDeckNames(receivedDecks)
+                })
+                .catch((error) => {
+                    console.error("Error fetching decks:", error)
+                })
+                .finally(() => {
+                    setIsLoadingDecks(false)
+                })
+        }
+    }, [playerAddress])
+
+    useEffect(() => {
+        loadDeckNames()
+    }, [loadDeckNames])
+
+    return (
+        <div className="flex w-full flex-col items-center p-3">
+            {/* New Deck Button */}
+            <Button
+                width="full"
+                className="my-2 border-2 border-yellow-500 font-fable text-xl hover:scale-105 hover:border-yellow-400"
+            >
+                <Link href={"/collection?newDeck=true"}>New Deck →</Link>
+            </Button>
+
+            {/* Loading Button */}
+            {isLoadingDecks && (
+                <Button
+                    width="full"
+                    className="my-2 border-2 border-yellow-500 font-fable text-xl normal-case hover:scale-105 hover:border-yellow-400"
+                    disabled={true}
+                >
+                    Loading...
+                </Button>
+            )}
+
+            {/* Deck Buttons */}
+            {deckNames.map((deckname, deckID) => (
+                <Button
+                    variant={"secondary"}
+                    width="full"
+                    className="my-1 border-2 border-yellow-500 font-fable text-xl normal-case hover:scale-105 hover:border-yellow-400"
+                    key={deckID}
+                    onClick={() => onDeckSelect(deckID)}
+                >
+                    {deckname}
+                </Button>
+            ))}
+        </div>
+    )
 }
 
-export default DeckCollectionDisplay
\ No newline at end of file
+export default DeckCollectionDisplay
diff --git a/packages/webapp/src/components/collection/deckPanel.tsx b/packages/webapp/src/components/collection/deckPanel.tsx
index 46c23f45..9699d065 100644
--- a/packages/webapp/src/components/collection/deckPanel.tsx
+++ b/packages/webapp/src/components/collection/deckPanel.tsx
@@ -62,14 +62,15 @@ const DeckConstructionPanel: React.FC<DeckConstructionPanelProps> = ({
             {/* Counter Row */}
             <div className="w-full py-1">
                 <div className="relative pt-1">
-                <div className="overflow-hidden h-2 text-xs flex rounded bg-red-200">
-                    <div style={{ width: `${(selectedCards.length / MAX_CARDS) * 100}%` }} 
-                    className={`shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center ${selectedCards.length < MIN_CARDS ? 'bg-red-500' : selectedCards.length <= MAX_CARDS ? 'bg-green-500' : 'bg-yellow-500'}`}>
+                    <div className="flex h-2 overflow-hidden rounded bg-red-200 text-xs">
+                        <div
+                            style={{ width: `${(selectedCards.length / MAX_CARDS) * 100}%` }}
+                            className={`flex flex-col justify-center whitespace-nowrap text-center text-white shadow-none ${selectedCards.length < MIN_CARDS ? "bg-red-500" : selectedCards.length <= MAX_CARDS ? "bg-green-500" : "bg-yellow-500"}`}
+                        ></div>
                     </div>
                 </div>
-                </div>
                 <div className="text-center text-sm font-medium">
-                {selectedCards.length}/{MAX_CARDS}
+                    {selectedCards.length}/{MAX_CARDS}
                 </div>
             </div>
 
@@ -77,7 +78,7 @@ const DeckConstructionPanel: React.FC<DeckConstructionPanelProps> = ({
             <div className="flex w-full flex-wrap justify-center gap-2">
                 <Button
                     variant="default"
-                    className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400"
+                    className="border-2 border-yellow-500 font-fable text-xl normal-case hover:scale-105 hover:border-yellow-400"
                     onClick={handleSave}
                 >
                     ✓ Save
diff --git a/packages/webapp/src/pages/collection.tsx b/packages/webapp/src/pages/collection.tsx
index d48f7803..fc61a276 100644
--- a/packages/webapp/src/pages/collection.tsx
+++ b/packages/webapp/src/pages/collection.tsx
@@ -1,23 +1,25 @@
+import React, { useEffect, useMemo, useState } from "react"
 import Head from "next/head"
+import { useRouter } from "next/router"
 
-import React, { useState, useMemo, useEffect, useCallback } from "react"
+import debounce from "lodash/debounce"
+import { navigate } from "utils/navigate"
 import { useAccount } from "wagmi"
 
+import { getDeck } from "src/actions/getDeck"
+import { modify, save } from "src/actions/setDeck"
+import CardCollectionDisplay from "src/components/collection/cardCollectionDisplay"
+import DeckList from "src/components/collection/deckList"
+import DeckPanel from "src/components/collection/deckPanel"
+import FilterPanel from "src/components/collection/filterPanel"
 import jotaiDebug from "src/components/lib/jotaiDebug"
+import { LoadingModal } from "src/components/modals/loadingModal"
 import { Navbar } from "src/components/navbar"
 import { deployment } from "src/deployment"
 import { useInventoryCardsCollectionGetCollection } from "src/generated"
 import { FablePage } from "src/pages/_app"
-import { Card, Deck } from "src/store/types"
-import { navigate } from "utils/navigate"
-import { getDeck } from "src/actions/getDeck"
-import FilterPanel from 'src/components/collection/filterPanel'
-import CardCollectionDisplay from 'src/components/collection/cardCollectionDisplay'
-import DeckList from 'src/components/collection/deckList'
-import DeckPanel from 'src/components/collection/deckPanel'
-import { save, modify } from "src/actions/setDeck"
 import * as store from "src/store/hooks"
-import { LoadingModal } from "src/components/modals/loadingModal"
+import { Card, Deck } from "src/store/types"
 
 // NOTE(norswap & geniusgarlic): Just an example, when the game actually has effects & types,
 //   fetch those from the chain instead of hardcoding them here.
@@ -46,9 +48,12 @@ const Collection: FablePage = ({ isHydrated }) => {
     const [isEditing, setIsEditing] = useState(false)
 
     // Deck Collection Display
-    const [ editingDeckIndex, setEditingDeckIndex ] = useState<number|null>(null)
+    const [editingDeckIndex, setEditingDeckIndex] = useState<number|null>(null)
     const [decks, setDecks] = useState<Deck[]>([])
-    const [ isLoadingDeck, setIsLoadingDeck ] = useState(false) 
+    const [isLoadingDeck, setIsLoadingDeck] = useState(false) 
+
+    const activeEffects = Object.keys(effectMap).filter((key) => effectMap[key])
+    const activeTypes = Object.keys(typeMap).filter((key) => typeMap[key])
 
     const { data: unfilteredCards } = useInventoryCardsCollectionGetCollection({
         address: deployment.InventoryCardsCollection,
@@ -71,66 +76,70 @@ const Collection: FablePage = ({ isHydrated }) => {
         )
     })
 
-  const handleInputChangeBouncy = (event: React.ChangeEvent<HTMLInputElement>) => {
-    setSearchInput(event.target.value)
-  }
-  const handleInputChange = useMemo(() => debounce(handleInputChangeBouncy, 300), [])
+    const handleInputChangeBouncy = (event: React.ChangeEvent<HTMLInputElement>) => {
+        setSearchInput(event.target.value)
+    }
+    const handleInputChange = useMemo(() => debounce(handleInputChangeBouncy, 300), [])
+
+    const handleEffectClick = (effectIndex: number) => {
+        const effect = effects[effectIndex]
+        setEffectMap({ ...effectMap, [effect]: !effectMap[effect] })
+    }
+
+    const handleTypeClick = (typeIndex: number) => {
+        const type = types[typeIndex]
+        setTypeMap({ ...typeMap, [type]: !typeMap[type] })
+    }
 
-  const handleEffectClick = (effectIndex: number) => {
-    const effect = effects[effectIndex]
-    setEffectMap({...effectMap, [effect]: !effectMap[effect]})
-  }
+    const handleSaveDeck = async (updatedDeck: Deck) => {
+        setIsSaving(true)
 
-  const handleTypeClick = (typeIndex: number) => {
-    const type = types[typeIndex]
-    setTypeMap({...typeMap, [type]: !typeMap[type]})
-  }
+        if (editingDeckIndex !== null) {
+            // Update existing deck
+            await modifyOnchain(updatedDeck, editingDeckIndex)
+        } else {
+            // Add the new deck to the list
+            await saveOnchain(updatedDeck)
+        }
 
-  const handleSaveDeck = async (updatedDeck: Deck) => {
-    setIsSaving(true)
+        setIsSaving(false)
 
-    if (editingDeckIndex !== null) {
-      // Update existing deck
-      await modifyOnchain(updatedDeck, editingDeckIndex)
-    } else {
-      // Add the new deck to the list
-      await saveOnchain(updatedDeck)
+        setIsEditing(false)
+        setSelectedCards([])
+        void navigate(router, "/collection")
     }
-    
-    setIsSaving(false)
 
-    setIsEditing(false)
-    setSelectedCards([])
-    void navigate(router, '/collection')
-  }
+    function saveOnchain(deck: Deck): Promise<void> {
+        return new Promise((resolve) => {
+            save({
+                deck,
+                playerAddress: playerAddress!,
+                onSuccess: () => {
+                    resolve()
+                },
+            })
+        })
+    }
 
-  function saveOnchain(deck: Deck): Promise<void> {
-    return new Promise((resolve) => {
-      save({
-        deck,
-        playerAddress: playerAddress!,
-        onSuccess: () => { resolve() }
-      })
-    })
-  }
+    function modifyOnchain(deck: Deck, editingDeckIndex: number): Promise<void> {
+        return new Promise((resolve) => {
+            modify({
+                deck,
+                playerAddress: playerAddress!,
+                index: BigInt(editingDeckIndex),
+                onSuccess: () => {
+                    resolve()
+                },
+            })
+        })
+    }
 
-  function modifyOnchain(deck: Deck, editingDeckIndex: number): Promise<void> {
-    return new Promise((resolve) => {
-      modify({
-        deck,
-        playerAddress: playerAddress!,
-        index: BigInt(editingDeckIndex),
-        onSuccess: () => { resolve() }
-      })
-    })
-  }
+    const handleCancelEditing = () => {
+        setIsEditing(false)
+        setSelectedCards([])
+        void navigate(router, "/collection")
+    }
 
-  const handleCancelEditing = () => {
-    setIsEditing(false)
-    setSelectedCards([])
-    void navigate(router, '/collection')
-  }
-  
     const addToDeck = (card: Card) => {
         setSelectedCards((prevSelectedCards) => {
             // Add or remove card from the selectedCards
@@ -156,39 +165,41 @@ const Collection: FablePage = ({ isHydrated }) => {
     }
 
     const handleDeckSelect = (deckID: number) => {
-      if (isLoadingDeck) return;
-      if (playerAddress) {
-        setIsLoadingDeck(true)
-        getDeck({
-          playerAddress: playerAddress,
-          index: deckID,
-          onSuccess: () => {
-          },
-        }).then(response => {
-          if(!response.simulatedResult) return;
-          
-  
-          const cardsReceived = response.simulatedResult.cards;
-          const cardObjects: Card[] = []
-          cardsReceived.forEach(card => {
-            const cID = Number(card)
-            const co = cards.find(c => Number(c.id) === cID)
-            if(co) { cardObjects.push(co) }
-          })
-          setSelectedCards(cardObjects);
-  
-          const deckName = response.simulatedResult.name;
-          setCurrentDeck({ name: deckName, cards: cardObjects })
-          setEditingDeckIndex(deckID)
-          setIsEditing(true)
-  
-        }).catch(error => {
-          console.error("Error fetching deck:", error);
-        }).finally(_ => {
-          setIsLoadingDeck(false)
-        })
-      }
-    }  
+        if (isLoadingDeck) return
+        if (playerAddress) {
+            setIsLoadingDeck(true)
+            getDeck({
+                playerAddress: playerAddress,
+                index: deckID,
+                onSuccess: () => {},
+            })
+                .then((response) => {
+                    if (!response.simulatedResult) return
+
+                    const cardsReceived = response.simulatedResult.cards
+                    const cardObjects: Card[] = []
+                    cardsReceived.forEach((card) => {
+                        const cID = Number(card)
+                        const co = cards.find((c) => Number(c.id) === cID)
+                        if (co) {
+                            cardObjects.push(co)
+                        }
+                    })
+                    setSelectedCards(cardObjects)
+
+                    const deckName = response.simulatedResult.name
+                    setCurrentDeck({ name: deckName, cards: cardObjects })
+                    setEditingDeckIndex(deckID)
+                    setIsEditing(true)
+                })
+                .catch((error) => {
+                    console.error("Error fetching deck:", error)
+                })
+                .finally((_) => {
+                    setIsLoadingDeck(false)
+                })
+        }
+    }
 
     // Sets up an event listener for route changes when deck editor is rendered.
     useEffect(() => {
@@ -214,53 +225,58 @@ const Collection: FablePage = ({ isHydrated }) => {
                 <title>0xFable: My Collection</title>
             </Head>
             {jotaiDebug()}
-            <main className="flex h-screen flex-col">
-                <Navbar />
-                <div className="mx-6 mb-6 grid min-h-0 grow grid-cols-12 gap-4">
-                    {/* Left Panel - Search and Filters */}
-                    <div className="col-span-3 flex overflow-y-auto rounded-xl border">
-                        <FilterPanel
-                            effects={effects}
-                            types={types}
-                            effectMap={effectMap}
-                            typeMap={typeMap}
-                            handleEffectClick={handleEffectClick}
-                            handleTypeClick={handleTypeClick}
-                            handleInputChange={handleInputChange}
-                            selectedCard={selectedCard}
-                        />
-                    </div>
-
-                    {/* Middle Panel - Card Collection Display */}
-                    <div className="col-span-7 flex overflow-y-auto rounded-xl border">
-                        <CardCollectionDisplay
-                            cards={cards}
-                            isHydrated={isHydrated}
-                            setSelectedCard={setSelectedCard}
-                            onCardToggle={onCardToggle}
-                            selectedCards={selectedCards}
-                            isEditing={isEditing}
-                        />
-                    </div>
-
-                    {/* Right Panel - Deck List */}
-                    <div className="col-span-2 flex overflow-y-auto rounded-xl border">
-                        {isEditing && currentDeck ? (
-                            <DeckPanel
-                                deck={currentDeck}
+            {isLoadingDeck ? (
+                <LoadingModal loading="Loading deck..." />
+            ) : (
+                <main className="flex h-screen flex-col">
+                    <Navbar />
+                    <div className="mx-6 mb-6 grid min-h-0 grow grid-cols-12 gap-4">
+                        {/* Left Panel - Search and Filters */}
+                        <div className="col-span-3 flex overflow-y-auto rounded-xl border">
+                            <FilterPanel
+                                effects={effects}
+                                types={types}
+                                effectMap={effectMap}
+                                typeMap={typeMap}
+                                handleEffectClick={handleEffectClick}
+                                handleTypeClick={handleTypeClick}
+                                handleInputChange={handleInputChange}
+                                selectedCard={selectedCard}
+                            />
+                        </div>
+                        {/* Middle Panel - Card Collection Display */}
+                        <div className="col-span-7 flex overflow-y-auto rounded-xl border">
+                            <CardCollectionDisplay
+                                cards={cards}
+                                isHydrated={isHydrated}
+                                setSelectedCard={setSelectedCard}
+                                onCardToggle={onCardToggle}
                                 selectedCards={selectedCards}
-                                onCardSelect={addToDeck}
-                                onSave={handleSaveDeck}
-                                onCancel={handleCancelEditing}
+                                isEditing={isEditing}
                             />
+                        </div>
+                        {/* Right Panel - Deck List */}
+                        {isSaving ? (
+                            <LoadingModal loading="Saving deck..." />
                         ) : (
-                            <DeckList decks={decks || []} onDeckSelect={handleDeckSelect} />
+                            <div className="col-span-2 flex overflow-y-auto rounded-xl border">
+                                {isEditing && currentDeck ? (
+                                    <DeckPanel
+                                        deck={currentDeck}
+                                        selectedCards={selectedCards}
+                                        onCardSelect={addToDeck}
+                                        onSave={handleSaveDeck}
+                                        onCancel={handleCancelEditing}
+                                    />
+                                ) : (
+                                    <DeckList decks={decks || []} onDeckSelect={handleDeckSelect} />
+                                )}
+                            </div>
                         )}
                     </div>
-                </div>
-            </main>
+                </main>
+            )}
         </>
     )
 }
-
 export default Collection