eleftherias's picture
Remove unused filters
695183e
import React, { useMemo, useEffect, useCallback } from "react";
import { Box, Typography } from "@mui/material";
import { useSearchParams } from "react-router-dom";
import { TABLE_DEFAULTS } from "./constants/defaults";
import { useLeaderboard } from "./context/LeaderboardContext";
import { useLeaderboardProcessing } from "./hooks/useLeaderboardData";
import { useLeaderboardData } from "./hooks/useLeaderboardData";
import LeaderboardTable from "./components/Table/Table";
import SearchBar, { SearchBarSkeleton } from "./components/Filters/SearchBar";
import PerformanceMonitor from "./components/PerformanceMonitor";
const Leaderboard = () => {
const { state, actions } = useLeaderboard();
const [searchParams, setSearchParams] = useSearchParams();
const {
data,
isLoading: dataLoading,
error: dataError,
} = useLeaderboardData();
const {
table,
filteredData,
error: processingError,
} = useLeaderboardProcessing();
// Memoize filtered data
const memoizedFilteredData = useMemo(() => filteredData, [filteredData]);
const memoizedTable = useMemo(() => table, [table]);
// Memoize table options
const hasTableOptionsChanges = useMemo(() => {
return (
state.display.rowSize !== TABLE_DEFAULTS.ROW_SIZE ||
JSON.stringify(state.display.scoreDisplay) !==
JSON.stringify(TABLE_DEFAULTS.SCORE_DISPLAY) ||
state.display.averageMode !== TABLE_DEFAULTS.AVERAGE_MODE ||
state.display.rankingMode !== TABLE_DEFAULTS.RANKING_MODE
);
}, [state.display]);
const hasColumnFilterChanges = useMemo(() => {
return (
JSON.stringify([...state.display.visibleColumns].sort()) !==
JSON.stringify([...TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE].sort())
);
}, [state.display.visibleColumns]);
// Memoize callbacks
const onToggleFilters = useCallback(() => {
actions.toggleFiltersExpanded();
}, [actions]);
const onColumnVisibilityChange = useCallback(
(newVisibility) => {
actions.setDisplayOption(
"visibleColumns",
Object.keys(newVisibility).filter((key) => newVisibility[key])
);
},
[actions]
);
const onRowSizeChange = useCallback(
(size) => {
actions.setDisplayOption("rowSize", size);
},
[actions]
);
const onScoreDisplayChange = useCallback(
(display) => {
actions.setDisplayOption("scoreDisplay", display);
},
[actions]
);
const onAverageModeChange = useCallback(
(mode) => {
actions.setDisplayOption("averageMode", mode);
},
[actions]
);
const onRankingModeChange = useCallback(
(mode) => {
actions.setDisplayOption("rankingMode", mode);
},
[actions]
);
const onPrecisionsChange = useCallback(
(precisions) => {
actions.setFilter("precisions", precisions);
},
[actions]
);
const onTypesChange = useCallback(
(types) => {
actions.setFilter("types", types);
},
[actions]
);
const onParamsRangeChange = useCallback(
(range) => {
actions.setFilter("paramsRange", range);
},
[actions]
);
const onBooleanFiltersChange = useCallback(
(filters) => {
actions.setFilter("booleanFilters", filters);
},
[actions]
);
const onReset = useCallback(() => {
actions.resetFilters();
}, [actions]);
// Memoize loading states
const loadingStates = useMemo(() => {
const isInitialLoading = dataLoading || !data;
const isProcessingData = !memoizedTable || !memoizedFilteredData;
const isApplyingFilters = state.models.length > 0 && !memoizedFilteredData;
const hasValidFilterCounts =
state.countsReady &&
state.filterCounts &&
state.filterCounts.normal &&
state.filterCounts.officialOnly;
return {
isInitialLoading,
isProcessingData,
isApplyingFilters,
showSearchSkeleton: isInitialLoading || !hasValidFilterCounts,
showFiltersSkeleton: isInitialLoading || !hasValidFilterCounts,
showTableSkeleton:
isInitialLoading ||
isProcessingData ||
isApplyingFilters ||
!hasValidFilterCounts,
};
}, [
dataLoading,
data,
memoizedTable,
memoizedFilteredData,
state.models.length,
state.filterCounts,
state.countsReady,
]);
// Memoize child components
const memoizedSearchBar = useMemo(
() => (
<SearchBar
onToggleFilters={onToggleFilters}
filtersOpen={state.filtersExpanded}
loading={loadingStates.showTableSkeleton}
data={memoizedFilteredData}
table={table}
/>
),
[
onToggleFilters,
state.filtersExpanded,
loadingStates.showTableSkeleton,
memoizedFilteredData,
table,
]
);
// No need to memoize LeaderboardTable as it handles its own sorting state
const tableComponent = (
<LeaderboardTable
table={table}
loading={loadingStates.showTableSkeleton}
onColumnVisibilityChange={onColumnVisibilityChange}
hasTableOptionsChanges={hasTableOptionsChanges}
hasColumnFilterChanges={hasColumnFilterChanges}
rowSize={state.display.rowSize}
onRowSizeChange={onRowSizeChange}
scoreDisplay={state.display.scoreDisplay}
onScoreDisplayChange={onScoreDisplayChange}
averageMode={state.display.averageMode}
onAverageModeChange={onAverageModeChange}
rankingMode={state.display.rankingMode}
onRankingModeChange={onRankingModeChange}
searchParams={searchParams}
setSearchParams={setSearchParams}
pinnedModels={state.pinnedModels}
/>
);
// Update context with loaded data
useEffect(() => {
if (data) {
actions.setModels(data);
}
}, [data, actions]);
// Log to understand loading state
useEffect(() => {
if (process.env.NODE_ENV === "development") {
console.log("Loading state:", {
dataLoading,
hasData: !!data,
hasTable: !!table,
hasFilteredData: !!filteredData,
filteredDataLength: filteredData?.length,
stateModelsLength: state.models.length,
hasFilters: Object.keys(state.filters).some((key) => {
if (Array.isArray(state.filters[key])) {
return state.filters[key].length > 0;
}
return !!state.filters[key];
}),
});
}
}, [
dataLoading,
data,
table,
filteredData?.length,
state.models.length,
filteredData,
state.filters,
]);
// If an error occurred, display it
if (dataError || processingError) {
return (
<Box sx={{ p: 3, textAlign: "center" }}>
<Typography color="error">
{(dataError || processingError)?.message ||
"An error occurred while loading the data"}
</Typography>
</Box>
);
}
return (
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
}}
>
<PerformanceMonitor />
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: 0,
alignItems: "center",
}}
>
<Box
sx={{
width: {
xs: "100%",
sm: "100%",
md: "80%",
},
maxWidth: "1200px",
}}
>
{loadingStates.showSearchSkeleton ? (
<SearchBarSkeleton />
) : (
memoizedSearchBar
)}
</Box>
<Box
sx={{
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
width: "100%",
overflow: "hidden",
}}
>
<Box
sx={{
width: "100%",
px: 1,
}}
>
{tableComponent}
</Box>
</Box>
</Box>
</Box>
);
};
export default Leaderboard;