Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/components/PredictionHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ export default function PredictionHistory({ userId, optimisticPrediction }: Pred
}, [history, userId]);

useEffect(() => {
void loadHistory();
Promise.resolve().then(() => {
void loadHistory();
});
}, [loadHistory]);

if (!userId) {
Expand Down Expand Up @@ -174,12 +176,12 @@ export default function PredictionHistory({ userId, optimisticPrediction }: Pred
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{(() => {
const raw = prediction.createdAt;
if (typeof raw !== "string") return "Unknown time";
const date = new Date(raw);
if (Number.isNaN(date.getTime())) return "Unknown time";
return formatRelativeTime(date);
})()}
const raw = prediction.createdAt;
if (typeof raw !== "string") return "Unknown time";
const date = new Date(raw);
if (Number.isNaN(date.getTime())) return "Unknown time";
return formatRelativeTime(date);
})()}
</p>
</div>

Expand Down
28 changes: 15 additions & 13 deletions src/components/PriceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,20 +487,22 @@ const PriceChart = ({ height = 300, asset = "XLM", entryPrice, onPriceUpdate }:

// Reload data when asset changes — reset and load mock/API data
useEffect(() => {
setData([]);
setIsLoading(true);
setLoadError(null);

// Use mock data directly for instant visual feedback per asset
const mockData = mockPriceData[asset];
if (mockData && mockData.length > 0) {
setData(mockData);
setLastUpdatedAt(new Date());
setIsLoading(false);
}
Promise.resolve().then(() => {
setData([]);
setIsLoading(true);
setLoadError(null);

// Use mock data directly for instant visual feedback per asset
const mockData = mockPriceData[asset];
if (mockData && mockData.length > 0) {
setData(mockData);
setLastUpdatedAt(new Date());
setIsLoading(false);
}

// Also attempt to fetch live data from API
void loadInitialPrices();
// Also attempt to fetch live data from API
void loadInitialPrices();
});
}, [asset]); // eslint-disable-line react-hooks/exhaustive-deps

// Update chart line color when asset changes
Expand Down
48 changes: 44 additions & 4 deletions src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { useRoundStore } from "../store/useRoundStore";
import type { Round, UserPrediction, UserStats } from "../lib/api-client";
import { educationApi, statsApi, predictionsApi } from "../lib/api-client";
import { useWalletStore, selectIsWalletConnected } from "../store/useWalletStore";
import StatusChip from "../components/hud/StatusChip";
import { TipCard } from "../components/education/TipCard";
import type { Tip } from "../types/education";
import EmptyState from '../components/EmptyState';
Expand Down Expand Up @@ -181,7 +182,9 @@ const activeRoundId = useRoundStore((state) => state.activeRound?.id ?? null);

// Clear the entry marker whenever the active round changes.
useEffect(() => {
setEntryPrice(null);
Promise.resolve().then(() => {
setEntryPrice(null);
});
}, [activeRoundId]);

const [stats, setStats] = useState<UserStats | null>(null);
Expand All @@ -194,6 +197,27 @@ const activeRoundId = useRoundStore((state) => state.activeRound?.id ?? null);
const [inspector, setInspector] = useState<SorobanInspectorSnapshot | null>(null);
const [isInspectorLoading, setIsInspectorLoading] = useState(false);
const [roundSoundEnabled, setRoundSoundEnabled] = useState(() => localStorage.getItem("xelma_round_sound") === "1");
const [practiceMode, setPracticeMode] = useState(() => localStorage.getItem("xelma_practice_mode") !== "false");

const prevWalletConnectedRef = useRef(isWalletConnected);
useEffect(() => {
if (!isWalletConnected) {
if (prevWalletConnectedRef.current) {
setPracticeMode(true);
localStorage.setItem("xelma_practice_mode", "true");
}
}
prevWalletConnectedRef.current = isWalletConnected;
}, [isWalletConnected]);

const effectivePracticeMode = !isWalletConnected || practiceMode;

const handlePracticeModeToggle = () => {
if (!isWalletConnected) return;
const nextMode = !practiceMode;
setPracticeMode(nextMode);
localStorage.setItem("xelma_practice_mode", String(nextMode));
};

// Asset tab state from URL query param
const [searchParams] = useSearchParams();
Expand Down Expand Up @@ -283,8 +307,10 @@ const activeRoundId = useRoundStore((state) => state.activeRound?.id ?? null);
}, [isWalletConnected, publicKey]);

useEffect(() => {
void fetchStats();
void fetchActivities();
Promise.resolve().then(() => {
void fetchStats();
void fetchActivities();
});
}, [fetchStats, fetchActivities]);

const refreshInspector = useCallback(async () => {
Expand All @@ -309,7 +335,9 @@ const activeRoundId = useRoundStore((state) => state.activeRound?.id ?? null);
}, [isWalletConnected, publicKey]);

useEffect(() => {
void refreshInspector();
Promise.resolve().then(() => {
void refreshInspector();
});
}, [refreshInspector]);

const handleRoundSoundToggle = (enabled: boolean) => {
Expand Down Expand Up @@ -402,6 +430,18 @@ const activeRoundId = useRoundStore((state) => state.activeRound?.id ?? null);

{!isLoading && (
<div className="mb-4 flex flex-wrap items-center justify-end gap-3">
<button
type="button"
data-testid="mode-chip"
onClick={handlePracticeModeToggle}
title={!isWalletConnected ? "Connect wallet to enable on-chain mode" : undefined}
className="inline-flex min-h-[40px] items-center gap-2 rounded-lg border border-white/10 bg-white/5 px-3 py-2 text-sm font-semibold transition-colors hover:border-white/20"
>
<StatusChip
label={effectivePracticeMode ? "Practice (vXLM)" : "On-Chain"}
status={effectivePracticeMode ? "info" : "active"}
/>
</button>
<label className="inline-flex min-h-[40px] items-center gap-2 rounded-lg border border-white/10 bg-white/5 px-4 py-2 text-sm text-gray-300">
<input
type="checkbox"
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Learn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ const LearnPage = () => {
}, []);

useEffect(() => {
fetchData();
Promise.resolve().then(() => {
fetchData();
});
}, [fetchData]);

if (loading) {
Expand Down