From 6abd115c813261c767eaf38fca36760a2abfb586 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Wed, 10 Jun 2026 00:12:14 +0200 Subject: [PATCH] client: market trend tags + leaderboard live updates via Socket.IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Market rows show a TrendTag: ๐Ÿ’ฐ affare when buyPrice < basePrice, ๐Ÿ“ˆ vendi when sellPrice > basePrice. Leaderboard subscribes to leaderboard:updated socket event to refresh without polling. Co-Authored-By: Claude Sonnet 4.6 --- client/src/screens/LeaderboardScreen.tsx | 12 ++++++++++++ client/src/screens/MarketScreen.tsx | 20 +++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/client/src/screens/LeaderboardScreen.tsx b/client/src/screens/LeaderboardScreen.tsx index a4d0eee..0c87ea8 100644 --- a/client/src/screens/LeaderboardScreen.tsx +++ b/client/src/screens/LeaderboardScreen.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import { api } from '../api/api'; import type { LeaderboardResponse } from '../api/types'; import { useAuth } from '../auth/AuthContext'; +import { getSocket } from '../realtime/socket'; import { formatMoney } from '../shared/format'; type Metric = 'money' | 'reputation'; @@ -16,6 +17,17 @@ export function LeaderboardScreen() { void api.leaderboard(metric).then(setBoard); }, [metric]); + // Aggiornamento live quando il server ricalcola le classifiche + useEffect(() => { + const socket = getSocket(); + if (!socket) return; + const onUpdate = () => void api.leaderboard(metric).then(setBoard); + socket.on('leaderboard:updated', onUpdate); + return () => { + socket.off('leaderboard:updated', onUpdate); + }; + }, [metric]); + return (

Classifiche

diff --git a/client/src/screens/MarketScreen.tsx b/client/src/screens/MarketScreen.tsx index 90488c1..c0cc976 100644 --- a/client/src/screens/MarketScreen.tsx +++ b/client/src/screens/MarketScreen.tsx @@ -7,6 +7,22 @@ import { useToast } from '../components/Toast'; import { getSocket } from '../realtime/socket'; import { formatMoney } from '../shared/format'; +/** + * Confronta i prezzi locali col valore base della merce: segnala dove + * conviene comprare (buy sotto il base) e dove conviene vendere (sell sopra). + */ +function TrendTag({ entry }: { entry: MarketEntry }) { + const buyDelta = Math.round((entry.buyPrice / entry.basePrice - 1) * 100); + const sellDelta = Math.round((entry.sellPrice / entry.basePrice - 1) * 100); + if (sellDelta > 0) { + return ๐Ÿ“ˆ vendi +{sellDelta}%; + } + if (buyDelta < 0) { + return ๐Ÿ’ฐ affare {buyDelta}%; + } + return null; +} + export function MarketScreen() { const { player, refreshPlayer } = useAuth(); const { toast } = useToast(); @@ -95,12 +111,14 @@ export function MarketScreen() {
{formatMoney(entry.buyPrice)} {formatMoney(entry.sellPrice)} +
))}

- Prezzo a sinistra: acquisto ยท a destra: vendita. I prezzi cambiano ogni minuto. + Prezzo a sinistra: acquisto ยท a destra: vendita. I prezzi cambiano ogni minuto: compra + dove c'รจ l'affare, vendi dove rende.

{selected && (