From bf8664b2f0db81789b135ab8b8850ac3d7896c01 Mon Sep 17 00:00:00 2001 From: Nisal Chandrasekara Date: Fri, 22 Aug 2025 18:51:31 +0530 Subject: [PATCH] feat: add btc price ticker --- src/app/dashboard/page.tsx | 6 +++ src/components/BtcPriceTicker.tsx | 72 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/components/BtcPriceTicker.tsx diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index b26da3f..fda13ae 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -20,6 +20,7 @@ import { LuArrowDownRight, LuArrowUpRight } from "react-icons/lu"; import { useQuery } from "@tanstack/react-query"; import { queryKeys } from "@/lib/query-keys"; import { RewardsChart } from "@/components/RewardsChart"; +import BtcPriceTicker from "@/components/BtcPriceTicker"; interface DCSummary { dca: { @@ -281,6 +282,11 @@ export default function WalletPage() { + {/* BTC Price Ticker */} +
+ +
+ {/* Balance Card */} {isLoading ? ( diff --git a/src/components/BtcPriceTicker.tsx b/src/components/BtcPriceTicker.tsx new file mode 100644 index 0000000..c03ec11 --- /dev/null +++ b/src/components/BtcPriceTicker.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { motion } from "framer-motion"; + +export default function BtcPriceTicker() { + const [price, setPrice] = useState(null); + const [prevPrice, setPrevPrice] = useState(null); + const [trend, setTrend] = useState<"up" | "down" | null>(null); + const priceRef = useRef(0); + const [rate, setRate] = useState(null); + + useEffect(() => { + const fetchRate = async () => { + try { + const res = await fetch("https://open.er-api.com/v6/latest/USD"); + const data = await res.json(); + setRate(data.rates.LKR); + } catch (err) { + console.error("Failed to fetch LKR rate", err); + } + }; + fetchRate(); + }, []); + + useEffect(() => { + if (!rate) return; + const ws = new WebSocket("wss://ws.coincap.io/prices?assets=bitcoin"); + ws.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data.bitcoin) { + priceRef.current = parseFloat(data.bitcoin) * rate; + } + }; + const interval = setInterval(() => { + setPrevPrice((p) => price); + setPrice(priceRef.current); + }, 1000); + return () => { + ws.close(); + clearInterval(interval); + }; + }, [rate, price]); + + useEffect(() => { + if (prevPrice === null || price === null) return; + if (price === prevPrice) return; + setTrend(price > prevPrice ? "up" : "down"); + const timeout = setTimeout(() => setTrend(null), 500); + return () => clearTimeout(timeout); + }, [price, prevPrice]); + + if (price === null) { + return
Loading BTC price...
; + } + + const trendClass = + trend === "up" ? "text-green-400" : trend === "down" ? "text-red-400" : "text-white"; + + return ( + + BTC Price: රු {price.toLocaleString("en-LK", { maximumFractionDigits: 0 })} + + ); +} +