From 9c989a46c8993a3d7a58dfbc375c72cfcc404d38 Mon Sep 17 00:00:00 2001 From: "Abdel @ StarkWare" Date: Thu, 10 Apr 2025 14:39:21 +0800 Subject: [PATCH 1/2] add holding in BTC directly in the page --- app/hooks/useBitcoinPrice.ts | 89 ++++++++++++++++++++++++ app/page.tsx | 129 ++++++++++++----------------------- 2 files changed, 131 insertions(+), 87 deletions(-) create mode 100644 app/hooks/useBitcoinPrice.ts diff --git a/app/hooks/useBitcoinPrice.ts b/app/hooks/useBitcoinPrice.ts new file mode 100644 index 0000000..d67ebb0 --- /dev/null +++ b/app/hooks/useBitcoinPrice.ts @@ -0,0 +1,89 @@ +import { useState, useEffect, useRef } from "react"; + +interface BitcoinPriceData { + bitcoinPrice: number; + priceDirection: string | null; + error: string | null; + countdown: number; + isFetching: boolean; +} + +export function useBitcoinPrice(): BitcoinPriceData { + const [bitcoinPrice, setBitcoinPrice] = useState(0); + const previousPriceRef = useRef(0); + const [priceDirection, setPriceDirection] = useState(null); + const [error, setError] = useState(null); + const [countdown, setCountdown] = useState(20); + const [isFetching, setIsFetching] = useState(true); // Start as true initially + + useEffect(() => { + const fetchBitcoinPrice = async () => { + setIsFetching(true); + setError(null); // Clear previous errors + try { + const response = await fetch( + "https://pricing.bitcoin.block.xyz/current-price" + ); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + const data = await response.json(); + const newPrice = parseFloat(data["amount"]); + + // Only update direction if not the very first fetch + if (previousPriceRef.current !== 0) { + if (newPrice > previousPriceRef.current) { + setPriceDirection("↑"); + } else if (newPrice < previousPriceRef.current) { + setPriceDirection("↓"); + } else { + setPriceDirection(null); // Explicitly set to null if equal + } + + // Reset direction indicator after a delay + if (newPrice !== previousPriceRef.current) { + setTimeout(() => { + setPriceDirection(null); + }, 2000); + } + } else { + setPriceDirection(null); // No direction on first load + } + + setBitcoinPrice(newPrice); + previousPriceRef.current = newPrice; // Update previous price *after* comparison + } catch (err) { + console.error("Failed to fetch Bitcoin price:", err); + setError("API Error"); // Simpler error message + setBitcoinPrice(0); // Reset price on error + setPriceDirection(null); + } finally { + setIsFetching(false); + setCountdown(20); // Reset countdown after fetch attempt + } + }; + + // Fetch immediately on load + fetchBitcoinPrice(); + + // Set up countdown interval + const countdownInterval = setInterval(() => { + setCountdown((prev) => { + if (prev <= 1) { + fetchBitcoinPrice(); // Fetch when countdown reaches 0 + return 20; // Reset to 20 seconds + } + return prev - 1; + }); + }, 1000); + + // Clean up intervals on component unmount + return () => { + clearInterval(countdownInterval); + }; + }, []); // Empty dependency array means this runs once on mount and cleans up on unmount + + return { bitcoinPrice, priceDirection, error, countdown, isFetching }; +} diff --git a/app/page.tsx b/app/page.tsx index e06c5ea..ba3dc35 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,8 +1,9 @@ "use client"; import { SolariBoard } from "./components/solari/SolariBoard"; -import { useState, useEffect, useMemo, useRef, Suspense } from "react"; +import { useState, useEffect, useMemo, Suspense } from "react"; import { useSearchParams, useRouter } from "next/navigation"; +import { useBitcoinPrice } from "./hooks/useBitcoinPrice"; // import { useDisplayLength } from "./components/useDisplayLength"; function formatCurrency(number: number, locale = "en-US", currency = "USD") { @@ -26,6 +27,7 @@ const getLoadingRows = (displayLength: number) => [ { value: "", length: displayLength }, { value: "", length: displayLength }, { value: "", length: displayLength }, + { value: "", length: displayLength }, ]; function HomeContent() { @@ -34,17 +36,31 @@ function HomeContent() { // const displayLength = useDisplayLength(); const displayLength = 20; // Fallback to a fixed length for simplicity - const [bitcoinPrice, setBitcoinPrice] = useState(0); - const previousPriceRef = useRef(0); - const [priceDirection, setPriceDirection] = useState(null); - const [holding] = useState(8485); + // Use the custom hook for price data + const { bitcoinPrice, priceDirection, error, countdown, isFetching } = + useBitcoinPrice(); + + // Get holding from URL, default to 40 + const getHoldingFromParams = () => { + const holdingParam = searchParams.get("holding"); + if (holdingParam) { + const parsedHolding = parseFloat(holdingParam); + // Validate if it's a positive number + if (!isNaN(parsedHolding) && parsedHolding > 0) { + return parsedHolding; + } + // Optionally: set an error or redirect if invalid? + // For now, just default back. + } + return 1; // Default value + }; + + const [holding, setHolding] = useState(getHoldingFromParams); const [holdingValue, setHoldingValue] = useState(0); const [currentRowIndex, setCurrentRowIndex] = useState(-1); const [ticker, setTicker] = useState(searchParams.get("ticker") || "XYZ"); - const [inputError, setInputError] = useState(null); - const [error, setError] = useState(null); - const [countdown, setCountdown] = useState(20); - const [isFetching, setIsFetching] = useState(false); + // Removed inputError as it wasn't used + // Removed explicit error, countdown, isFetching states - now handled by hook // Initialize loading rows immediately const loadingBoardRows = useMemo( @@ -57,14 +73,18 @@ function HomeContent() { setHoldingValue(bitcoinPrice * holding); }, [bitcoinPrice, holding]); + // Update holding if URL param changes + useEffect(() => { + setHolding(getHoldingFromParams()); + }, [searchParams]); // Re-run when searchParams change + // Format the display values const displayValue = error - ? "Error" - : `${formatCurrency(bitcoinPrice).toString()}${ - priceDirection ? ` ${priceDirection}` : "" - }`; + ? error // Display the error message from the hook + : `${formatCurrency(bitcoinPrice).toString()}${priceDirection ? ` ${priceDirection}` : ""}`; const holdingDisplay = error ? "Error" : formatCurrency(holdingValue); + const holdingDisplayBTC = error ? "Error" : `${holding} BTC`; // Define the final board rows const finalBoardRows = useMemo( @@ -72,14 +92,15 @@ function HomeContent() { { value: "", length: displayLength }, { value: ` ${ticker}`, length: displayLength }, { value: "", length: displayLength }, - { value: " TOTAL HOLDING", length: displayLength }, + { value: ` ${holdingDisplayBTC}`, length: displayLength, color: "#FFA500" }, + { value: " TOTAL HOLDING USD", length: displayLength }, { value: ` ${holdingDisplay}`, length: displayLength }, { value: "", length: displayLength }, { value: " BTC PRICE", length: displayLength }, { value: ` ${displayValue}`, length: displayLength }, { value: "", length: displayLength }, ], - [ticker, holdingDisplay, displayValue, displayLength] + [ticker, holdingDisplay, holdingDisplayBTC, displayValue, displayLength] ); // Current board rows based on loading state and animation progress @@ -98,7 +119,8 @@ function HomeContent() { // Handle the row-by-row animation useEffect(() => { - if (!isFetching && currentRowIndex === -1) { + // Start animation only when initial fetch is done AND no error + if (!isFetching && currentRowIndex === -1 && !error) { // Start the row animation after data is loaded const animateRows = () => { const interval = setInterval(() => { @@ -117,80 +139,13 @@ function HomeContent() { // Small delay before starting the animation setTimeout(animateRows, 1000); } - }, [isFetching, currentRowIndex, finalBoardRows.length]); - - // Fetch Bitcoin price and manage countdown - useEffect(() => { - const fetchBitcoinPrice = async () => { - setIsFetching(true); - try { - const response = await fetch( - "https://pricing.bitcoin.block.xyz/current-price" - ); - - if (!response.ok) { - throw new Error(`API error: ${response.status}`); - } - - const data = await response.json(); - const newPrice = parseFloat(data["amount"]); - - // Check if this is not the first fetch - if (!isFetching) { - // Compare with previous price to determine direction - if (newPrice > previousPriceRef.current) { - setPriceDirection("↑"); - } else if (newPrice < previousPriceRef.current) { - setPriceDirection("↓"); - } else { - setPriceDirection(null); - } - - // Remove the direction indicator after 5 seconds (increased from 2 seconds) - if (newPrice !== previousPriceRef.current) { - setTimeout(() => { - setPriceDirection(null); - }, 2000); - } - } else { - // Set initial price without showing direction - setPriceDirection(null); - } - - // Update prices - const oldPrice = previousPriceRef.current; - previousPriceRef.current = newPrice; - setBitcoinPrice(newPrice); - } catch (err) { - console.error("Failed to fetch Bitcoin price:", err); - setError("Failed to fetch Bitcoin price"); - } - setIsFetching(false); - setCountdown(20); - }; - - // Fetch immediately on load - fetchBitcoinPrice(); - - // Set up countdown interval - const countdownInterval = setInterval(() => { - setCountdown((prev) => { - if (prev <= 1) { - fetchBitcoinPrice(); // Fetch when countdown reaches 0 - return 20; // Reset to 20 seconds - } - return prev - 1; - }); - }, 1000); - - // Clean up intervals on component unmount - return () => { - clearInterval(countdownInterval); - }; - }, []); + }, [isFetching, currentRowIndex, finalBoardRows.length, error]); // Added error dependency return (
+ {/* Input field for holding - Optional feature */} + {/* Consider adding an input field here if direct editing is desired */} +
From 344b7a9fbb27f75a0d6f8f063d7e1309ee4b53fa Mon Sep 17 00:00:00 2001 From: "Abdel @ StarkWare" Date: Thu, 10 Apr 2025 14:40:00 +0800 Subject: [PATCH 2/2] put pack XYZ default holding value --- app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index ba3dc35..c870185 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -52,7 +52,7 @@ function HomeContent() { // Optionally: set an error or redirect if invalid? // For now, just default back. } - return 1; // Default value + return 8485; // Default value }; const [holding, setHolding] = useState(getHoldingFromParams);