Skip to content

Commit

Permalink
Add Nostr page and TradingView widget; update Navbar and package depe…
Browse files Browse the repository at this point in the history
…ndencies
  • Loading branch information
miladsoft committed Jan 8, 2025
1 parent e74a43a commit 3625607
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 93 deletions.
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"bech32": "^2.0.0",
"framer-motion": "^11.11.10",
"gsap": "^3.12.5",
"lightweight-charts": "^4.2.2",
"nostr-tools": "^2.10.4",
"npm": "^10.9.0",
"npm-check-updates": "^17.1.6",
Expand Down
156 changes: 111 additions & 45 deletions src/components/BtcPriceWidget.jsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,137 @@
import { useEffect, useState, useRef } from "react";
import { useEffect, useState, useRef, useCallback } from "react";
import gsap from "gsap";
import ChartWidget from "./ChartWidget";

const BtcPriceWidget = () => {
const [price, setPrice] = useState("Loading...");
const [change, setChange] = useState("Loading...");
const [changeColor, setChangeColor] = useState("#fff");
const priceRef = useRef(null);
const previousPrice = useRef(null);
const wsRef = useRef(null);

useEffect(() => {
const ws = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt@ticker");
const MAX_RECONNECT_ATTEMPTS = 5;
const reconnectAttemptsRef = useRef(0);

const fetchPriceData = async () => {
try {
const response = await fetch('https://api.binance.com/api/v3/ticker/24hr?symbol=BTCUSDT');
const data = await response.json();

const currentPrice = parseFloat(data.lastPrice).toFixed(2);
const priceChangePercent = parseFloat(data.priceChangePercent).toFixed(2);

setPrice(`$${currentPrice}`);
setChange(`24h Change: ${priceChangePercent}%`);
setChangeColor(priceChangePercent >= 0 ? "#27ae60" : "#e74c3c");
previousPrice.current = currentPrice;
animatePrice();
} catch (error) {
console.error('Failed to fetch price data:', error);
}
};

ws.onopen = () => {
console.log("WebSocket connection opened");
};
const animatePrice = useCallback(() => {
if (priceRef.current) {
gsap.fromTo(
priceRef.current,
{ scale: 1.1, opacity: 0.8 },
{ scale: 1, opacity: 1, duration: 0.3, ease: "power2.out" }
);
}
}, []);

ws.onmessage = (event) => {
const data = JSON.parse(event.data);
const currentPrice = parseFloat(data.c).toFixed(2);
const priceChangePercent = parseFloat(data.P).toFixed(2);
const initializeWebSocket = useCallback(() => {
if (wsRef.current) {
wsRef.current.close();
}

if (previousPrice.current !== currentPrice) {
gsap.fromTo(
priceRef.current,
{ scale: 1.2, opacity: 0.5 },
{ scale: 1, opacity: 1, duration: 0.5, ease: "elastic.out(1, 0.3)" }
);
try {
const ws = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt@ticker");
wsRef.current = ws;

setPrice(`$${currentPrice}`);
setChange(`24h Change: ${priceChangePercent}%`);
ws.onopen = () => {
console.log("WebSocket connection opened");
reconnectAttemptsRef.current = 0; // Reset counter on successful connection
};

setChangeColor(priceChangePercent >= 0 ? "#27ae60" : "#e74c3c");
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
const currentPrice = parseFloat(data.c).toFixed(2);
const priceChangePercent = parseFloat(data.P).toFixed(2);

previousPrice.current = currentPrice;
}
};
if (previousPrice.current !== currentPrice) {
setPrice(`$${currentPrice}`);
setChange(`24h Change: ${priceChangePercent}%`);
setChangeColor(priceChangePercent >= 0 ? "#27ae60" : "#e74c3c");
previousPrice.current = currentPrice;
animatePrice();
}
} catch (error) {
console.error('Error processing WebSocket message:', error);
}
};

ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
fetchPriceData(); // Fallback to REST API on error
};

ws.onclose = () => {
console.log("WebSocket connection closed. Reconnecting...");
setTimeout(() => {
initializeWebSocket();
}, 1000);
};
ws.onclose = () => {
console.log("WebSocket connection closed");
if (reconnectAttemptsRef.current < MAX_RECONNECT_ATTEMPTS) {
reconnectAttemptsRef.current++;
console.log(`Reconnecting... Attempt ${reconnectAttemptsRef.current}`);
setTimeout(initializeWebSocket, 2000 * reconnectAttemptsRef.current);
} else {
console.log('Max reconnection attempts reached. Falling back to REST API');
// Set up polling fallback
const intervalId = setInterval(fetchPriceData, 5000);
return () => clearInterval(intervalId);
}
};

return ws;
} catch (error) {
console.error('Failed to initialize WebSocket:', error);
fetchPriceData();
}
}, [animatePrice]);

useEffect(() => {
fetchPriceData(); // Initial price fetch
const ws = initializeWebSocket();

return () => {
ws.close();
if (ws && ws.readyState === WebSocket.OPEN) {
ws.close();
}
};
}, []);
}, [initializeWebSocket]);

return (
<div className="flex flex-col items-center justify-center mb-24 md:mb-48 lg:mb-48 text-white">
<div
ref={priceRef}
className="text-4xl md:text-6xl lg:text-8xl font-extrabold transition-colors duration-500"
style={{ color: changeColor }}
>
{price}
<div className="flex flex-col items-center w-full gap-6">
<div className="text-center">
<div
ref={priceRef}
className="text-4xl md:text-6xl lg:text-8xl font-extrabold transition-colors duration-500"
style={{ color: changeColor }}
>
{price}
</div>
<div
className="text-lg md:text-xl lg:text-2xl mt-2"
style={{ color: changeColor }}
>
{change}
</div>
</div>
<div
className="text-lg md:text-xl lg:text-2xl mt-2 md:mt-3 lg:mt-4"
style={{ color: changeColor }}
>
{change}

<div className="w-full max-w-[1400px] mx-auto px-4 mb-20">
<div className="w-full bg-bgDark1 rounded-lg shadow-xl overflow-hidden">
<ChartWidget />
</div>
</div>
</div>
);
Expand Down
Loading

0 comments on commit 3625607

Please sign in to comment.