Skip to content

Commit a9f4945

Browse files
Merge pull request #81 from StabilityNexus/Frontend
Pagination Cats fetching fixed
2 parents bf5f480 + 250f771 commit a9f4945

File tree

3 files changed

+87
-132
lines changed

3 files changed

+87
-132
lines changed

web/src/app/create/page.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,9 @@ export default function CreateCAT() {
225225
message: "CAT contract deployed successfully!",
226226
});
227227

228-
// Trigger sync notification for my-cats page
229-
localStorage.setItem('catCreated', JSON.stringify({
230-
timestamp: Date.now(),
231-
chainId: config.state.chainId,
232-
hash: deployData
233-
}));
234-
235-
// Dispatch custom event for immediate sync if my-cats page is open
236-
window.dispatchEvent(new CustomEvent('catCreated', {
237-
detail: { chainId: config.state.chainId, hash: deployData }
238-
}));
239-
240228
// Add a small delay before redirecting to ensure the blockchain state is updated
241229
setTimeout(() => {
242-
router.push("/my-cats?sync=true");
230+
router.push("/my-cats");
243231
setIsDeploying(false);
244232
}, 2000);
245233
} catch (error) {

web/src/app/my-cats/page.tsx

Lines changed: 85 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { CatRoleDropdown } from "../../components/CatRoleDropdown";
1818
import { useCATStorage } from "@/hooks/useCATStorage";
1919
import { SupportedChainId, CatDetails as StoredCatDetails } from "@/utils/indexedDB";
2020
import toast from "react-hot-toast";
21-
import { useRouter, useSearchParams } from "next/navigation";
21+
import { useRouter } from "next/navigation";
2222

2323
// Define supported chain IDs - use the imported type from IndexedDB
2424
// type SupportedChainId = 137 | 534351 | 5115 | 61 | 8453;
@@ -67,13 +67,14 @@ const isValidChainId = (
6767

6868
export default function MyCATsPage() {
6969
const [currentPageCATs, setCurrentPageCATs] = useState<CatDetails[]>([]);
70+
const [allFilteredCATs, setAllFilteredCATs] = useState<CatDetails[]>([]); // Cache filtered CATs
7071
const [isLoading, setIsLoading] = useState(true);
7172
const [error, setError] = useState<string | null>(null);
7273
const [searchQuery, setSearchQuery] = useState("");
7374
const [selectedChainId, setSelectedChainId] = useState<SupportedChainId | "all">("all");
7475
const [roleFilter, setRoleFilter] = useState<"all" | "creator" | "minter">("all");
7576
const [pagination, setPagination] = useState<PaginationInfo>({
76-
currentPage: 1,
77+
currentPage: 0,
7778
totalPages: 0,
7879
totalCreatorCATs: 0,
7980
totalMinterCATs: 0,
@@ -86,7 +87,6 @@ export default function MyCATsPage() {
8687
const { address } = useAccount();
8788
const currentChainId = useChainId();
8889
const router = useRouter();
89-
const searchParams = useSearchParams();
9090
const {
9191
getAllCatDetailsForUser,
9292
getCatDetailsByRole,
@@ -375,63 +375,75 @@ export default function MyCATsPage() {
375375
};
376376
}, [isOnline, address, syncWithBlockchain]);
377377

378-
// Fetch CATs for a specific page using storage-first approach
379-
const fetchCATsForPage = useCallback(async (page: number): Promise<CatDetails[]> => {
380-
if (!address || !isInitialized) return [];
378+
// Update filtered CATs and pagination when filters change
379+
const updateFilteredCATs = useCallback(async () => {
380+
if (!address || !isInitialized) return;
381381

382382
try {
383-
// Load all filtered CATs from storage
384-
const allStoredCATs = await loadCATsFromStorage();
383+
const filteredCATs = await loadCATsFromStorage();
384+
setAllFilteredCATs(filteredCATs);
385385

386-
// Calculate pagination indices
387-
const catsPerPage = pagination.catsPerPage;
388-
const startIndex = (page - 1) * catsPerPage;
389-
const endIndex = startIndex + catsPerPage;
386+
// Calculate pagination
387+
const totalCATs = filteredCATs.length;
388+
const creatorCount = filteredCATs.filter(cat => cat.userRole === 'admin').length;
389+
const minterCount = filteredCATs.filter(cat => cat.userRole === 'minter').length;
390+
const totalPages = Math.ceil(totalCATs / pagination.catsPerPage);
391+
const firstPage = totalPages > 0 ? 1 : 0;
390392

391-
// Return the page slice
392-
return allStoredCATs.slice(startIndex, endIndex);
393+
setPagination(prev => ({
394+
...prev,
395+
totalPages,
396+
totalCreatorCATs: creatorCount,
397+
totalMinterCATs: minterCount,
398+
currentPage: firstPage, // Reset to first page when filters change
399+
}));
400+
401+
// Show first page - early return with empty slice when no pages
402+
const firstPageCATs = totalPages === 0 ? [] : filteredCATs.slice(0, pagination.catsPerPage);
403+
setCurrentPageCATs(firstPageCATs);
393404
} catch (error) {
394-
console.error("Error fetching CATs for page from storage:", error);
395-
return [];
405+
console.error('Error updating filtered CATs:', error);
406+
setError('Failed to load CATs. Please try again.');
396407
}
397408
}, [address, isInitialized, loadCATsFromStorage, pagination.catsPerPage]);
398409

399-
// Handle page navigation with storage-first approach
400-
const goToPage = useCallback(async (page: number) => {
401-
if (page < 1 || page > pagination.totalPages || page === pagination.currentPage) return;
402-
403-
try {
404-
setIsLoading(true);
405-
406-
// Load page data from storage
407-
const pageCATs = await fetchCATsForPage(page);
408-
setCurrentPageCATs(pageCATs);
409-
setPagination(prev => ({ ...prev, currentPage: page }));
410-
} catch (error) {
411-
console.error("Error navigating to page:", error);
412-
setError("Failed to load page. Please try again.");
413-
} finally {
414-
setIsLoading(false);
410+
// Update current page CATs when page changes (without refetching from storage)
411+
const updateCurrentPageCATs = useCallback((page: number) => {
412+
// Early return with empty slice when no pages
413+
if (pagination.totalPages === 0 || page <= 0) {
414+
setCurrentPageCATs([]);
415+
return;
415416
}
416-
}, [pagination.totalPages, pagination.currentPage, fetchCATsForPage]);
417+
418+
const startIndex = (page - 1) * pagination.catsPerPage;
419+
const endIndex = startIndex + pagination.catsPerPage;
420+
const pageCATs = allFilteredCATs.slice(startIndex, endIndex);
421+
setCurrentPageCATs(pageCATs);
422+
}, [allFilteredCATs, pagination.catsPerPage, pagination.totalPages]);
423+
424+
// Handle page navigation with cached data
425+
const goToPage = useCallback((page: number) => {
426+
if (pagination.totalPages === 0 || page < 1 || page > pagination.totalPages || page === pagination.currentPage) return;
427+
428+
setPagination(prev => ({ ...prev, currentPage: page }));
429+
updateCurrentPageCATs(page);
430+
}, [pagination.totalPages, pagination.currentPage, updateCurrentPageCATs]);
417431

418432
const goToPreviousPage = () => goToPage(pagination.currentPage - 1);
419433
const goToNextPage = () => goToPage(pagination.currentPage + 1);
420434

421-
// Filter and search function
422-
const filteredCATs = currentPageCATs?.filter((cat) => {
423-
const matchesSearch = searchQuery === "" ||
424-
cat.tokenName.toLowerCase().includes(searchQuery.toLowerCase()) ||
425-
cat.tokenSymbol.toLowerCase().includes(searchQuery.toLowerCase());
426-
427-
const matchesChain = selectedChainId === "all" || cat.chainId === Number(selectedChainId);
428-
429-
const matchesRole = roleFilter === "all" ||
430-
(roleFilter === "creator" && cat.userRole === "admin") ||
431-
(roleFilter === "minter" && cat.userRole === "minter");
432-
433-
return matchesSearch && matchesChain && matchesRole;
434-
});
435+
// Update filtered CATs when filters change
436+
useEffect(() => {
437+
updateFilteredCATs();
438+
}, [updateFilteredCATs]);
439+
440+
// Update current page CATs when pagination current page changes
441+
useEffect(() => {
442+
updateCurrentPageCATs(pagination.currentPage);
443+
}, [pagination.currentPage, updateCurrentPageCATs]);
444+
445+
// Display CATs are now managed in state, no additional filtering needed
446+
const filteredCATs = currentPageCATs;
435447

436448
// Initialize pagination with IndexedDB integration (offline-first approach)
437449
const initializePagination = useCallback(async () => {
@@ -448,85 +460,53 @@ export default function MyCATsPage() {
448460
const storedCATs = await loadCATsFromStorage();
449461

450462
if (storedCATs.length > 0) {
451-
// Calculate pagination from stored data
463+
// Cache the filtered CATs and update pagination
464+
setAllFilteredCATs(storedCATs);
465+
452466
const totalCATs = storedCATs.length;
453467
const creatorCount = storedCATs.filter(cat => cat.userRole === 'admin').length;
454468
const minterCount = storedCATs.filter(cat => cat.userRole === 'minter').length;
455469
const totalPages = Math.ceil(totalCATs / pagination.catsPerPage);
470+
const firstPage = totalPages > 0 ? 1 : 0;
456471

457472
setPagination(prev => ({
458473
...prev,
459474
totalPages,
460475
totalCreatorCATs: creatorCount,
461476
totalMinterCATs: minterCount,
462-
currentPage: 1,
477+
currentPage: firstPage,
463478
}));
464479

465-
// Show first page from stored data
466-
const startIndex = 0;
467-
const endIndex = pagination.catsPerPage;
468-
setCurrentPageCATs(storedCATs.slice(startIndex, endIndex));
480+
// Show first page from cached data - early return with empty slice when no pages
481+
const firstPageCATs = totalPages === 0 ? [] : storedCATs.slice(0, pagination.catsPerPage);
482+
setCurrentPageCATs(firstPageCATs);
469483

470484
// Show data immediately from storage
471485
setIsLoading(false);
472486

473487
// Then sync with blockchain in background if online
474488
if (isOnline) {
475489
syncWithBlockchain(false).then(async () => {
476-
// Refresh data after successful sync
477-
const refreshedCATs = await loadCATsFromStorage();
478-
if (refreshedCATs.length !== storedCATs.length) {
479-
// Data changed, refresh the display
480-
const newTotalCATs = refreshedCATs.length;
481-
const newCreatorCount = refreshedCATs.filter(cat => cat.userRole === 'admin').length;
482-
const newMinterCount = refreshedCATs.filter(cat => cat.userRole === 'minter').length;
483-
const newTotalPages = Math.ceil(newTotalCATs / pagination.catsPerPage);
484-
485-
setPagination(prev => ({
486-
...prev,
487-
totalPages: newTotalPages,
488-
totalCreatorCATs: newCreatorCount,
489-
totalMinterCATs: newMinterCount,
490-
}));
491-
492-
const newStartIndex = 0;
493-
const newEndIndex = pagination.catsPerPage;
494-
setCurrentPageCATs(refreshedCATs.slice(newStartIndex, newEndIndex));
495-
}
490+
// Refresh data after successful sync by triggering updateFilteredCATs
491+
await updateFilteredCATs();
496492
}).catch(console.error);
497493
}
498494
} else {
499495
// No stored data, must fetch from blockchain
500496
if (isOnline) {
501497
await syncWithBlockchain(true); // Force sync
502-
// Reload from storage after sync
503-
const newStoredCATs = await loadCATsFromStorage();
504-
505-
const totalCATs = newStoredCATs.length;
506-
const creatorCount = newStoredCATs.filter(cat => cat.userRole === 'admin').length;
507-
const minterCount = newStoredCATs.filter(cat => cat.userRole === 'minter').length;
508-
const totalPages = Math.ceil(totalCATs / pagination.catsPerPage);
509-
510-
setPagination(prev => ({
511-
...prev,
512-
totalPages,
513-
totalCreatorCATs: creatorCount,
514-
totalMinterCATs: minterCount,
515-
currentPage: 1,
516-
}));
517-
518-
const startIndex = 0;
519-
const endIndex = pagination.catsPerPage;
520-
setCurrentPageCATs(newStoredCATs.slice(startIndex, endIndex));
498+
// Trigger update after sync
499+
await updateFilteredCATs();
521500
} else {
522501
setError("No data available offline. Please connect to the internet to sync your CATs.");
502+
setAllFilteredCATs([]);
523503
setCurrentPageCATs([]);
524504
setPagination(prev => ({
525505
...prev,
526506
totalPages: 0,
527507
totalCreatorCATs: 0,
528508
totalMinterCATs: 0,
529-
currentPage: 1,
509+
currentPage: 0,
530510
}));
531511
}
532512
}
@@ -537,24 +517,13 @@ export default function MyCATsPage() {
537517
} finally {
538518
setIsLoading(false);
539519
}
540-
}, [address, isInitialized, storageError, loadCATsFromStorage, syncWithBlockchain, isOnline, pagination.catsPerPage]);
520+
}, [address, isInitialized, storageError, loadCATsFromStorage, syncWithBlockchain, isOnline, pagination.catsPerPage, updateFilteredCATs]);
541521

542522
useEffect(() => {
543523
initializePagination();
544524
}, [initializePagination]);
545525

546-
// Handle sync URL parameter from create page redirect
547-
useEffect(() => {
548-
const shouldSync = searchParams.get('sync');
549-
if (shouldSync === 'true' && isOnline && address && isInitialized) {
550-
console.log('Sync parameter detected, triggering immediate sync...');
551-
toast.success('Welcome back! Syncing your new CAT...');
552-
syncWithBlockchain(true).then(() => {
553-
// Clear the sync parameter from URL
554-
router.replace('/my-cats', { scroll: false });
555-
}).catch(console.error);
556-
}
557-
}, [searchParams, isOnline, address, isInitialized, syncWithBlockchain, router]);
526+
558527

559528
// Helper function to add delays between requests
560529
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
@@ -803,18 +772,18 @@ export default function MyCATsPage() {
803772
>
804773
<motion.button
805774
onClick={goToPreviousPage}
806-
disabled={pagination.currentPage === 1}
775+
disabled={pagination.currentPage <= 1 || pagination.totalPages === 0}
807776
className="flex items-center gap-2 px-4 py-2 rounded-xl bg-white/80 dark:bg-[#1a1400]/70 border border-[#bfdbfe] dark:border-yellow-400/20 text-gray-800 dark:text-yellow-100 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-50 dark:hover:bg-yellow-400/10 transition-all duration-300"
808-
whileHover={{ scale: pagination.currentPage === 1 ? 1 : 1.05 }}
809-
whileTap={{ scale: pagination.currentPage === 1 ? 1 : 0.95 }}
777+
whileHover={{ scale: (pagination.currentPage <= 1 || pagination.totalPages === 0) ? 1 : 1.05 }}
778+
whileTap={{ scale: (pagination.currentPage <= 1 || pagination.totalPages === 0) ? 1 : 0.95 }}
810779
>
811780
<ChevronLeft className="w-4 h-4" />
812781
<span>Previous</span>
813782
</motion.button>
814783

815784
<div className="flex items-center gap-2">
816785
<span className="text-sm text-gray-600 dark:text-yellow-200">
817-
Page {pagination.currentPage} of {pagination.totalPages}
786+
{pagination.totalPages === 0 ? 'No pages' : `Page ${pagination.currentPage} of ${pagination.totalPages}`}
818787
</span>
819788
<span className="text-xs text-gray-500 dark:text-yellow-200/70">
820789
({pagination.totalCreatorCATs + pagination.totalMinterCATs} total CATs)
@@ -823,10 +792,10 @@ export default function MyCATsPage() {
823792

824793
<motion.button
825794
onClick={goToNextPage}
826-
disabled={pagination.currentPage === pagination.totalPages}
795+
disabled={pagination.currentPage >= pagination.totalPages || pagination.totalPages === 0}
827796
className="flex items-center gap-2 px-4 py-2 rounded-xl bg-white/80 dark:bg-[#1a1400]/70 border border-[#bfdbfe] dark:border-yellow-400/20 text-gray-800 dark:text-yellow-100 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-50 dark:hover:bg-yellow-400/10 transition-all duration-300"
828-
whileHover={{ scale: pagination.currentPage === pagination.totalPages ? 1 : 1.05 }}
829-
whileTap={{ scale: pagination.currentPage === pagination.totalPages ? 1 : 0.95 }}
797+
whileHover={{ scale: (pagination.currentPage >= pagination.totalPages || pagination.totalPages === 0) ? 1 : 1.05 }}
798+
whileTap={{ scale: (pagination.currentPage >= pagination.totalPages || pagination.totalPages === 0) ? 1 : 0.95 }}
830799
>
831800
<span>Next</span>
832801
<ChevronRight className="w-4 h-4" />
@@ -889,19 +858,17 @@ export default function MyCATsPage() {
889858
animate={{ opacity: 1, y: 0 }}
890859
transition={{ duration: 0.5, delay: index * 0.1 }}
891860
>
892-
<div className="absolute inset-0 bg-gradient-to-r from-[#93c5fd]/30 to-[#60a5fa]/30 dark:from-yellow-400/20 dark:to-blue-400/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-300"></div>
861+
<div className="absolute inset-0 bg-gradient-to-r from-[#93c5fd]/30 to-[#60a5fa]/30 dark:from-yellow-400/20 dark:to-yellow-400/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all duration-300"></div>
893862
<motion.div
894863
className="relative rounded-2xl p-8 bg-white/80 dark:bg-[#1a1400]/70 border border-[#bfdbfe] dark:border-yellow-400/20 backdrop-blur-lg transition-all duration-300 hover:scale-105 hover:shadow-[0_8px_32px_0_rgba(37,99,235,0.25)] dark:hover:shadow-[0_8px_32px_0_rgba(255,217,0,0.25)] hover:border-blue-300 dark:hover:border-yellow-400"
895864
whileHover={{ y: -8 }}
896865
whileTap={{ scale: 0.98 }}
897866
>
898867
<div className="relative z-10 flex flex-col">
899868
<div className="flex items-center justify-between mb-6">
900-
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-blue-500 to-blue-300 dark:from-[#FFD600] dark:to-blue-400 flex items-center justify-center text-white font-bold text-xl">
901-
{cat.tokenSymbol.slice(0, 2)}
902-
</div>
869+
903870
<div className="flex-1 text-center px-4">
904-
<h2 className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-500 to-blue-300 dark:from-[#FFD600] dark:to-blue-400">
871+
<h2 className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-500 to-blue-300 dark:from-[#FFD600] dark:to-white">
905872
{cat.tokenName || cat.address}
906873
</h2>
907874
<p className="text-sm text-[#1e40af] dark:text-yellow-100">

web/src/components/CatRoleDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const ROLE_OPTIONS: RoleOption[] = [
3232
{
3333
value: "minter",
3434
label: "Minter CATs",
35-
description: "CATs with minter role only",
35+
description: "CATs with minter role",
3636
icon: <Hammer className="h-4 w-4" />,
3737
color: "bg-gradient-to-r from-orange-500/20 to-red-500/20"
3838
}

0 commit comments

Comments
 (0)