diff --git a/app/controllers/api/v0/competitions_controller.rb b/app/controllers/api/v0/competitions_controller.rb
index d37b345614..4fa21ad8da 100644
--- a/app/controllers/api/v0/competitions_controller.rb
+++ b/app/controllers/api/v0/competitions_controller.rb
@@ -25,7 +25,7 @@ def competition_index
competitions = competitions_scope.search(params[:q], params: params)
- serial_methods = ["short_display_name", "city", "country_iso2", "event_ids", "date_range", "latitude_degrees", "longitude_degrees"]
+ serial_methods = ["short_display_name", "city", "country_iso2", "event_ids", "date_range", "latitude_degrees", "longitude_degrees", "cached_registration_status"]
serial_includes = {}
serial_includes["delegates"] = { only: ["id", "name"], methods: [], include: ["avatar"] } if admin_mode
diff --git a/app/models/competition.rb b/app/models/competition.rb
index 358ad74b69..1e95a247ea 100644
--- a/app/models/competition.rb
+++ b/app/models/competition.rb
@@ -1008,6 +1008,12 @@ def registration_status
end
end
+ def cached_registration_status
+ Rails.cache.fetch(["comp-registration-status", self.id], expires_in: 5.minutes) do
+ self.registration_status
+ end
+ end
+
def any_registrations?
self.registrations.any?
end
diff --git a/app/webpacker/components/CompetitionsOverview/CompetitionsFilters.js b/app/webpacker/components/CompetitionsOverview/CompetitionsFilters.js
index 9145b98a90..b2f8c8c85d 100644
--- a/app/webpacker/components/CompetitionsOverview/CompetitionsFilters.js
+++ b/app/webpacker/components/CompetitionsOverview/CompetitionsFilters.js
@@ -16,6 +16,7 @@ import UtcDatePicker from '../wca/UtcDatePicker';
function CompetitionsFilters({
filterState,
dispatchFilter,
+ displayMode,
shouldShowAdminDetails,
canViewAdminDetails,
}) {
@@ -30,17 +31,17 @@ function CompetitionsFilters({
/>
-
-
+
+
-
+
{shouldShowAdminDetails && (
-
+
)}
@@ -49,12 +50,20 @@ function CompetitionsFilters({
+
+
+
+
{canViewAdminDetails && shouldShowAdminDetails && (
-
-
-
-
-
+
+
+
)}
);
@@ -428,8 +437,6 @@ function CustomDateSelector({ filterState, dispatchFilter }) {
export function CompDisplayCheckboxes({
shouldIncludeCancelled,
dispatchFilter,
- shouldShowRegStatus,
- setShouldShowRegStatus,
shouldShowAdminDetails,
canViewAdminDetails,
displayMode,
@@ -449,32 +456,19 @@ export function CompDisplayCheckboxes({
{
- displayMode === 'list' && (
- <>
-
-
setShouldShowRegStatus(!shouldShowRegStatus)}
- />
-
- {canViewAdminDetails && (
-
-
dispatchFilter(
- { shouldShowAdminDetails: !shouldShowAdminDetails },
- )}
- />
-
- )}
- >
+ displayMode === 'list' && canViewAdminDetails && (
+
+
dispatchFilter(
+ { shouldShowAdminDetails: !shouldShowAdminDetails },
+ )}
+ />
+
)
}
>
diff --git a/app/webpacker/components/CompetitionsOverview/CompetitionsView.js b/app/webpacker/components/CompetitionsOverview/CompetitionsView.js
index 859df26b0c..45d33caaf0 100644
--- a/app/webpacker/components/CompetitionsOverview/CompetitionsView.js
+++ b/app/webpacker/components/CompetitionsOverview/CompetitionsView.js
@@ -1,11 +1,10 @@
import React, {
useEffect, useMemo, useReducer, useState,
} from 'react';
-import { keepPreviousData, useInfiniteQuery, useQuery } from '@tanstack/react-query';
+import { useInfiniteQuery } from '@tanstack/react-query';
import {
Button,
Container,
- Form,
Header,
Icon,
Segment,
@@ -16,7 +15,7 @@ import I18n from '../../lib/i18n';
import { apiV0Urls, WCA_API_PAGINATION } from '../../lib/requests/routes.js.erb';
import { fetchJsonOrError } from '../../lib/requests/fetchWithAuthenticityToken';
-import CompetitionsFilters, { CompDisplayCheckboxes, ToggleListOrMapDisplay } from './CompetitionsFilters';
+import CompetitionsFilters, { ToggleListOrMapDisplay } from './CompetitionsFilters';
import ListView from './ListView';
import MapView from './MapView';
import {
@@ -44,7 +43,7 @@ function CompetitionsView({ canViewAdminDetails = false }) {
);
const debouncedFilterState = useDebounce(filterState, DEBOUNCE_MS);
const [displayMode, setDisplayMode] = useState(() => getDisplayMode(searchParams));
- const [shouldShowRegStatus, setShouldShowRegStatus] = useState(false);
+
const competitionQueryKey = useMemo(
() => calculateQueryKey(debouncedFilterState, canViewAdminDetails),
[debouncedFilterState, canViewAdminDetails],
@@ -84,35 +83,7 @@ function CompetitionsView({ canViewAdminDetails = false }) {
},
});
- const baseCompetitions = rawCompetitionData?.pages.flatMap((page) => page.data);
- const compIds = baseCompetitions?.map((comp) => comp.id) || [];
-
- const {
- data: compRegistrationData,
- isFetching: regDataIsPending,
- } = useQuery({
- queryFn: () => fetchJsonOrError(apiV0Urls.competitions.registrationData, {
- headers: {
- 'Content-Type': 'application/json',
- },
- method: 'POST',
- body: JSON.stringify({ ids: compIds }),
- }),
- queryKey: ['registration-info', ...compIds],
- enabled: shouldShowRegStatus && compIds.length > 0,
- // This is where the magic happens: Using `keepPreviousData` makes it so that
- // all previously loaded indicators are held in-cache while the fetcher for the next
- // batch is running in the background. (Adding comment here because it's not in the docs)
- placeholderData: keepPreviousData,
- select: (data) => data.data,
- });
-
- const competitions = useMemo(() => (shouldShowRegStatus ? (
- baseCompetitions?.map((comp) => {
- const regData = compRegistrationData?.find((reg) => reg.id === comp.id);
- return regData ? { ...comp, ...regData } : comp;
- })
- ) : baseCompetitions), [baseCompetitions, compRegistrationData, shouldShowRegStatus]);
+ const competitions = rawCompetitionData?.pages.flatMap((page) => page.data);
const [showFilters, setShowFilters] = useState(true);
@@ -153,29 +124,15 @@ function CompetitionsView({ canViewAdminDetails = false }) {
dispatchFilter={dispatchFilter}
shouldShowAdminDetails={shouldShowAdminDetails}
canViewAdminDetails={canViewAdminDetails}
+ displayMode={displayMode}
/>
-
-
-
-
-
-
-
+
{
@@ -184,10 +141,8 @@ function CompetitionsView({ canViewAdminDetails = false }) {
diff --git a/app/webpacker/components/CompetitionsOverview/ListView.js b/app/webpacker/components/CompetitionsOverview/ListView.js
index 4bbd66e5a5..0eff01641f 100644
--- a/app/webpacker/components/CompetitionsOverview/ListView.js
+++ b/app/webpacker/components/CompetitionsOverview/ListView.js
@@ -11,10 +11,8 @@ import { isInProgress, isProbablyOver } from '../../lib/utils/competition-table'
function ListView({
competitions,
filterState,
- shouldShowRegStatus,
shouldShowAdminDetails,
isLoading,
- regStatusLoading,
fetchMoreCompetitions,
hasMoreCompsToLoad,
}) {
@@ -39,10 +37,8 @@ function ListView({
@@ -68,9 +64,7 @@ function ListView({
@@ -78,9 +72,7 @@ function ListView({
@@ -99,11 +91,9 @@ function ListView({
@@ -163,11 +149,9 @@ function ListView({
) : (
@@ -59,8 +55,6 @@ function ListViewSection({
competitions={competitions}
isLoading={isLoading}
hasMoreCompsToLoad={hasMoreCompsToLoad}
- shouldShowRegStatus={shouldShowRegStatus}
- regStatusLoading={regStatusLoading}
isSortedByAnnouncement={isSortedByAnnouncement}
/>
)}
@@ -73,8 +67,6 @@ function ResponsiveCompetitionsTables({
competitions,
isLoading,
hasMoreCompsToLoad,
- shouldShowRegStatus,
- regStatusLoading,
isSortedByAnnouncement,
}) {
const noCompetitions = !competitions || competitions.length === 0;
@@ -91,8 +83,6 @@ function ResponsiveCompetitionsTables({
@@ -100,8 +90,6 @@ function ResponsiveCompetitionsTables({
@@ -109,8 +97,6 @@ function ResponsiveCompetitionsTables({
@@ -120,8 +106,6 @@ function ResponsiveCompetitionsTables({
export function CompetitionsTable({
competitions,
- shouldShowRegStatus,
- regStatusLoading,
isSortedByAnnouncement = false,
}) {
return (
@@ -148,9 +132,7 @@ export function CompetitionsTable({
@@ -177,8 +159,6 @@ export function CompetitionsTable({
export function CompetitionsTabletTable({
competitions,
- shouldShowRegStatus,
- regStatusLoading,
isSortedByAnnouncement = false,
}) {
return (
@@ -204,9 +184,7 @@ export function CompetitionsTabletTable({
@@ -233,8 +211,6 @@ export function CompetitionsTabletTable({
export function CompetitionsMobileTable({
competitions,
- shouldShowRegStatus,
- regStatusLoading,
isSortedByAnnouncement = false,
}) {
return (
@@ -253,9 +229,7 @@ export function CompetitionsMobileTable({
@@ -288,9 +262,7 @@ function AdminCompetitionsTable({
competitions,
isLoading,
hasMoreCompsToLoad,
- shouldShowRegStatus,
selectedDelegate,
- regStatusLoading,
isSortedByAnnouncement,
}) {
const noCompetitions = !competitions || competitions.length === 0;
@@ -333,9 +305,7 @@ function AdminCompetitionsTable({
@@ -437,15 +407,8 @@ function ConditionalYearHeader({
}
}
-function RegistrationStatus({ comp, isLoading }) {
- // It is important that we check both conditions, because the query hook
- // uses a `keepPreviousData` trick that holds existing data in-memory while
- // also executing the query for the next batch of rows in the background.
- if (isLoading && !comp.registration_status) {
- return ();
- }
-
- if (comp.registration_status === 'not_yet_opened') {
+function RegistrationStatus({ comp }) {
+ if (comp.cached_registration_status === 'not_yet_opened') {
return (
}
@@ -455,7 +418,7 @@ function RegistrationStatus({ comp, isLoading }) {
/>
);
}
- if (comp.registration_status === 'past') {
+ if (comp.cached_registration_status === 'past') {
return (
}
@@ -465,7 +428,7 @@ function RegistrationStatus({ comp, isLoading }) {
/>
);
}
- if (comp.registration_status === 'full') {
+ if (comp.cached_registration_status === 'full') {
return (
}
@@ -475,7 +438,7 @@ function RegistrationStatus({ comp, isLoading }) {
/>
);
}
- if (comp.registration_status === 'open') {
+ if (comp.cached_registration_status === 'open') {
return (
}
@@ -493,9 +456,7 @@ function RegistrationStatus({ comp, isLoading }) {
function StatusIcon({
comp,
- shouldShowRegStatus,
isSortedByAnnouncement,
- regStatusLoading,
}) {
let tooltipInfo = '';
let iconClass = '';
@@ -511,14 +472,11 @@ function StatusIcon({
} else if (isInProgress(comp)) {
tooltipInfo = I18n.t('competitions.index.tooltips.hourglass.in_progress');
iconClass = 'hourglass half';
- } else if (shouldShowRegStatus) {
- return ;
} else if (isSortedByAnnouncement) {
tooltipInfo = I18n.t('competitions.index.tooltips.hourglass.announced_on', { announcement_date: comp.announced_at });
iconClass = 'hourglass start';
} else {
- tooltipInfo = I18n.t('competitions.index.tooltips.hourglass.starts_in', { days: I18n.t('common.days', { count: dayDifferenceFromToday(comp.start_date) }) });
- iconClass = 'hourglass start';
+ return ;
}
return (