fix: resolve bugs and improve frontend performance
- Fix register not resetting isLoading on success (causing login page to hang) - Fix leaderboard streaks 400 error by forcing all_time timeframe - Reorder routes so static paths match before dynamic practice/:sheetId - Lazy-load QuestMap + Three.js (saves ~350KB gzip on initial load) - Move KaTeX CSS to lazy import (only loads on math pages) - Remove 28 duplicate Google Font @import lines from component CSS - Add font preconnect + single stylesheet link in index.html - Replace 8 unsafe JSON.parse(localStorage) calls with Zustand selectors - Add global ErrorBoundary to prevent full-app crashes - Extract arcTheme utilities to break static import cycle with QuestMap - Merge Three.js + Troika into single chunk to fix circular dependency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -31,7 +31,6 @@ const DOTS = [
|
||||
];
|
||||
|
||||
const STYLES = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap');
|
||||
|
||||
:root { --content-max: 1100px; }
|
||||
|
||||
@ -415,6 +414,7 @@ const EmptyState = () => (
|
||||
|
||||
export const Rewards = () => {
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const token = useAuthStore((state) => state.token);
|
||||
const [time, setTime] = useState("today");
|
||||
const [activeTab, setActiveTab] = useState<TabId>("xp");
|
||||
const [leaderboard, setLeaderboard] = useState<Leaderboard | undefined>();
|
||||
@ -431,20 +431,15 @@ export const Rewards = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!user) return;
|
||||
const authStorage = localStorage.getItem("auth-storage");
|
||||
if (!authStorage) return;
|
||||
const parsed = JSON.parse(authStorage) as {
|
||||
state?: { token?: string };
|
||||
} | null;
|
||||
const token = parsed?.state?.token;
|
||||
if (!token) return;
|
||||
if (!user || !token) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
const timeframe =
|
||||
activeTab === "streaks" ? "all_time" : (TIME_MAP[time] ?? "daily");
|
||||
const response = await api.fetchLeaderboard(
|
||||
token,
|
||||
activeTab,
|
||||
TIME_MAP[time] ?? "daily",
|
||||
timeframe,
|
||||
);
|
||||
setLeaderboard(response);
|
||||
// ✅ FIX 1: Guard against null user_rank before accessing its properties
|
||||
@ -563,9 +558,13 @@ export const Rewards = () => {
|
||||
</div>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="rw-filter-btn">
|
||||
{formatTimeLabel(time)} <ChevronDown size={13} />
|
||||
<DropdownMenuTrigger asChild disabled={activeTab === "streaks"}>
|
||||
<button
|
||||
className="rw-filter-btn"
|
||||
style={activeTab === "streaks" ? { opacity: 0.5, cursor: "not-allowed" } : undefined}
|
||||
>
|
||||
{activeTab === "streaks" ? "All Time" : formatTimeLabel(time)}{" "}
|
||||
<ChevronDown size={13} />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
|
||||
Reference in New Issue
Block a user