Skip to content

Commit

Permalink
fix canvas loading
Browse files Browse the repository at this point in the history
  • Loading branch information
joaolago1113 committed Jan 6, 2025
1 parent d8573ff commit 5a6f3ba
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 111 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

- **CollaborativeArtCanvas.sol:**
- **Description:** Handles the canvas state, pixel color updates, and interactions with PaintTokens, enabling users to paint and own pixels securely.
- **Deployment Address:** [0x1e952234E4014B836FfA8877b26Cb61fF206926e](https://block-explorer.testnet.lens.dev/address/0x1e952234E4014B836FfA8877b26Cb61fF206926e)
- **Deployment Address:** [0xDdC944c362A3D3b8B2223092Fab0027DF127ae27](https://block-explorer.testnet.lens.dev/address/0xDdC944c362A3D3b8B2223092Fab0027DF127ae27)

- **PaintToken.sol:**
- **Description:** Manages the creation, distribution, and burning of PaintTokens, ensuring a controlled token economy within the platform.
- **Deployment Address:** [0xDdC944c362A3D3b8B2223092Fab0027DF127ae27](https://block-explorer.testnet.lens.dev/address/0xDdC944c362A3D3b8B2223092Fab0027DF127ae27)
- **Deployment Address:** [0x1e952234E4014B836FfA8877b26Cb61fF206926e](https://block-explorer.testnet.lens.dev/address/0x1e952234E4014B836FfA8877b26Cb61fF206926e)

### Frontend Integration

Expand Down
69 changes: 43 additions & 26 deletions packages/nextjs/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useMemo, useState, useCallback, useEffect, useRef } from "react"
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import { useReadContract } from "wagmi";
import { CONTRACTS, CANVAS_ABI } from "~/constants/contracts";
import { useCanvasData } from "~/hooks/useCanvasData";

interface CanvasProps {
onPixelClick: (pixelId: number) => void;
Expand All @@ -28,24 +29,16 @@ export function Canvas({
const [isDrawing, setIsDrawing] = useState(false);
const canvasRef = useRef<HTMLDivElement>(null);

const { data: canvasWidth } = useReadContract({
address: CONTRACTS.COLLABORATIVE_ART_CANVAS,
abi: CANVAS_ABI,
functionName: "CANVAS_WIDTH",
});
// Add loading state tracking
const [loadingAttempts, setLoadingAttempts] = useState(0);
const maxAttempts = 3;

const { data: canvasHeight } = useReadContract({
address: CONTRACTS.COLLABORATIVE_ART_CANVAS,
abi: CANVAS_ABI,
functionName: "CANVAS_HEIGHT",
});
// Define loading state
const isLoading = !pixelOwners;

const totalPixels = useMemo(() => {
if (canvasWidth && canvasHeight) {
return Number(canvasWidth) * Number(canvasHeight);
}
return 0;
}, [canvasWidth, canvasHeight]);
return 64 * 64; // Hardcode dimensions since we know them
}, []);

const handlePixelHover = useCallback((pixelId: number) => {
setHoveredPixel(pixelId);
Expand All @@ -57,7 +50,7 @@ export function Canvas({

// Calculate pixel size and scale based on container
useEffect(() => {
if (!canvasRef.current || !canvasWidth) return;
if (!canvasRef.current) return;

const updateSize = () => {
const GRID_DIMENSION = 64;
Expand Down Expand Up @@ -99,7 +92,7 @@ export function Canvas({
resizeObserver.unobserve(canvasRef.current);
}
};
}, [canvasWidth, isInitialLoad]);
}, [isInitialLoad]);

const handleMouseDown = (pixelId: number) => {
setIsDrawing(true);
Expand Down Expand Up @@ -185,21 +178,45 @@ export function Canvas({
return () => window.removeEventListener('wheel', preventDefault);
}, []);

// Consolidate loading checks
const isLoading = !canvasWidth || !canvasHeight || !pixelOwners;
const hasNoData = !pixelOwners || Object.keys(pixelOwners).length === 0;
// Add debug logging for loading state
useEffect(() => {
console.log("[Canvas] Loading state:", {
hasPixelOwners: !!pixelOwners,
pixelOwnersCount: pixelOwners ? Object.keys(pixelOwners).length : 0,
isLoading,
loadingAttempts
});
}, [pixelOwners, isLoading, loadingAttempts]);

useEffect(() => {
if (pixelOwners && Object.keys(pixelOwners).length > 0) {
console.log("[Canvas] Rendering with pixel data:", Object.keys(pixelOwners).length);
if (isLoading && loadingAttempts < maxAttempts) {
const timer = setTimeout(() => {
setLoadingAttempts(prev => prev + 1);
}, 1000);
return () => clearTimeout(timer);
}
}, [pixelOwners]);
}, [isLoading, loadingAttempts]);

if (isLoading || hasNoData) {
console.log("[Canvas] Loading state:", { isLoading, hasNoData, pixelOwnersCount: Object.keys(pixelOwners || {}).length });
if (isLoading) {
if (loadingAttempts >= maxAttempts) {
return (
<div className="flex flex-col items-center justify-center h-64 text-red-500">
<p>Failed to load canvas data</p>
<button
onClick={() => window.location.reload()}
className="mt-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
Retry
</button>
</div>
);
}
return (
<div className="flex items-center justify-center h-64">
<div className="flex flex-col items-center justify-center h-64">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary"></div>
<p className="mt-4 text-sm text-gray-500">
Loading canvas... ({loadingAttempts + 1}/{maxAttempts})
</p>
</div>
);
}
Expand Down
128 changes: 45 additions & 83 deletions packages/nextjs/hooks/useCanvasData.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,67 @@
import { useEffect, useState, useRef } from "react";
import { useEffect, useState } from "react";
import { useReadContract } from "wagmi";
import { CONTRACTS, CANVAS_ABI } from "~/constants/contracts";

export function useCanvasData() {
const [totalPixelsPainted, setTotalPixelsPainted] = useState<number>(0);
const [pixelOwners, setPixelOwners] = useState<{ [key: number]: { color: number } }>({});
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const [isInitialized, setIsInitialized] = useState(false);

const retryCountRef = useRef(0);
const maxRetries = 10;

const { data: pixels, isError, error: contractError, refetch, isLoading: isContractLoading } = useReadContract({
// Separate dimension queries
const { data: canvasWidth } = useReadContract({
address: CONTRACTS.COLLABORATIVE_ART_CANVAS,
abi: CANVAS_ABI,
functionName: "getAllPixels",
}) as {
data: bigint[];
isError: boolean;
error: Error | null;
refetch: () => Promise<any>;
isLoading: boolean;
};

useEffect(() => {
const retryInterval = 2000;
functionName: "CANVAS_WIDTH",
});

const attemptFetch = () => {
if (!isInitialized && retryCountRef.current < maxRetries) {
console.log(`[Canvas] Retry ${retryCountRef.current + 1}/${maxRetries}, Pixels: ${!!pixels}, Error: ${!!isError}`);

const timer = setTimeout(() => {
retryCountRef.current += 1;
refetch();
}, retryInterval);

return () => clearTimeout(timer);
}
};
const { data: canvasHeight } = useReadContract({
address: CONTRACTS.COLLABORATIVE_ART_CANVAS,
abi: CANVAS_ABI,
functionName: "CANVAS_HEIGHT",
});

return attemptFetch();
}, [pixels, isError, refetch, isInitialized]);
const { data: pixels, isError, isLoading: isContractLoading } = useReadContract({
address: CONTRACTS.COLLABORATIVE_ART_CANVAS,
abi: CANVAS_ABI,
functionName: "getAllPixels",
}) as { data: bigint[] | undefined; isError: boolean; isLoading: boolean };

useEffect(() => {
if (isError) {
console.error("[Canvas] Error fetching canvas data:", contractError);
setError(contractError || new Error("Failed to fetch canvas data"));
return;
}

if (!pixels) {
console.log("[Canvas] No pixel data available yet");
return;
}

try {
console.log("[Canvas] Processing pixel data, length:", pixels.length);
const owners: { [key: number]: { color: number } } = {};
let paintedCount = 0;

pixels.forEach((pixel, index) => {
if (pixel !== BigInt(0)) {
const colorValue = Number(pixel);
owners[index] = { color: colorValue };
paintedCount++;
const processPixels = async () => {
try {
if (!pixels || !canvasWidth || !canvasHeight) {
console.log("[useCanvasData] Waiting for data...", {
hasPixels: !!pixels,
hasWidth: !!canvasWidth,
hasHeight: !!canvasHeight
});
return;
}
});

console.log("[Canvas] Processed pixels:", {
total: pixels.length,
painted: paintedCount,
ownersSize: Object.keys(owners).length
});
const processedPixels: { [key: number]: { color: number } } = {};
pixels.forEach((color, index) => {
if (color > 0) {
processedPixels[index] = { color: Number(color) };
}
});

setPixelOwners(owners);
setTotalPixelsPainted(paintedCount);
setError(null);
setIsInitialized(true);
console.log("[Canvas] Data initialization complete");
} catch (err) {
console.error("[Canvas] Error processing pixel data:", err);
setError(err instanceof Error ? err : new Error("Failed to process pixel data"));
}
}, [pixels, isError, contractError]);
setPixelOwners(processedPixels);
setIsLoading(false);
} catch (err) {
console.error("[useCanvasData] Error processing pixels:", err);
setError(err instanceof Error ? err : new Error("Failed to process pixels"));
setIsLoading(false);
}
};

useEffect(() => {
console.log("[Canvas] State update:", {
hasPixels: !!pixels,
pixelOwnersCount: Object.keys(pixelOwners).length,
isLoading: (!pixels && !isError) || !isInitialized || isContractLoading,
hasError: !!error,
isInitialized
});
}, [pixels, pixelOwners, error, isInitialized, isContractLoading, isError]);
processPixels();
}, [pixels, canvasWidth, canvasHeight]);

return {
totalPixelsPainted,
pixelOwners,
isLoading: (!pixels && !isError) || !isInitialized || isContractLoading,
error,
refetch,
isInitialized
canvasWidth: canvasWidth ? Number(canvasWidth) : undefined,
canvasHeight: canvasHeight ? Number(canvasHeight) : undefined,
isLoading: isLoading || isContractLoading || !canvasWidth || !canvasHeight,
error: error || (isError ? new Error("Failed to fetch data") : null)
};
}

0 comments on commit 5a6f3ba

Please sign in to comment.