diff --git a/frontend/src/components/App.tsx b/frontend/src/components/App.tsx index 0d448055..6171393f 100644 --- a/frontend/src/components/App.tsx +++ b/frontend/src/components/App.tsx @@ -6,13 +6,14 @@ import {useDate} from "../context/date-context"; import {SnackbarMessage} from "../types/ui"; import {backend} from "../util/util"; import {AboutInformation} from "./dialogs/AboutInformation"; +import {LeafletWorldMap} from "./leaflet/LeafletWorldMap"; +import {ExternalLink} from "./link/ExternalLink"; import {LoadingAnimation} from "./loading/LoadingAnimation"; -// Lazy loaded components -const ExternalLink = React.lazy(() => import('./link/ExternalLink')); +// Lazy loaded components. The map is part of the entry (it is always needed), so its chunk is requested together with +// the entry script instead of after it. The overlay has its own Suspense boundary, so the map does not wait for it. const OverlayLarge = React.lazy(() => import('./overlay/OverlayLarge')); const OverlaySmall = React.lazy(() => import('./overlay/OverlaySmall')); -const LeafletWorldMap = React.lazy(() => import('./leaflet/LeafletWorldMap')); export const App: FunctionComponent = () => { // Component state @@ -46,34 +47,34 @@ export const App: FunctionComponent = () => { return ( <> - }> - - {isLoading ? : null} - - - + + {isLoading ? : null} + + + + {isLargeScreen ? : } - - - - {" | © "} - - {" contributors © "} - - - - - - + + + + + {" | © "} + + {" contributors © "} + + + + + ) } diff --git a/frontend/src/components/dialogs/relay/ResponsiveRelayDetailsDialog.tsx b/frontend/src/components/dialogs/relay/ResponsiveRelayDetailsDialog.tsx index 6c86dec1..f94039ec 100644 --- a/frontend/src/components/dialogs/relay/ResponsiveRelayDetailsDialog.tsx +++ b/frontend/src/components/dialogs/relay/ResponsiveRelayDetailsDialog.tsx @@ -114,8 +114,11 @@ export const ResponsiveRelayDetailsDialog: FunctionComponent = () => { } } - // Discard the previous relays as soon as the relays of another location are passed to the dialog - const [syncedRelayDetailsIdToLocationMap, setSyncedRelayDetailsIdToLocationMap] = useState(relayDetailsIdToLocationMap) + // Discard the previous relays as soon as the relays of another location are passed to the dialog. + // Starts without a value, because the dialog is only mounted when it is opened for the first time, so the relays of + // that first location have to count as a change too. + const [syncedRelayDetailsIdToLocationMap, setSyncedRelayDetailsIdToLocationMap] = + useState>() if (syncedRelayDetailsIdToLocationMap !== relayDetailsIdToLocationMap) { setSyncedRelayDetailsIdToLocationMap(relayDetailsIdToLocationMap) setRelayDetailsMatch(undefined) diff --git a/frontend/src/components/leaflet/LeafletLayers.tsx b/frontend/src/components/leaflet/LeafletLayers.tsx index c17061a5..f8d245df 100644 --- a/frontend/src/components/leaflet/LeafletLayers.tsx +++ b/frontend/src/components/leaflet/LeafletLayers.tsx @@ -56,16 +56,22 @@ export const LeafletLayers: FunctionComponent = ({relays, reloadSelectedD // Component state const [showFamilySelectionDialog, setShowFamilySelectionDialog] = useState(false) const [familiesForSelectionDialog, setFamiliesForSelectionDialog] = useState([]) + // The dialogs are separate chunks and are only loaded when they are opened for the first time. They stay mounted + // afterwards, so they can close with their transition. + const [hasOpenedRelayDetailsDialog, setHasOpenedRelayDetailsDialog] = useState(false) + const [hasOpenedFamilySelectionDialog, setHasOpenedFamilySelectionDialog] = useState(false) const [leafletMarkerLayers] = useState(new LayerGroup()) // The country borders are a separate chunk, which is only loaded once the countries are shown const [worldFeatures, setWorldFeatures] = useState() // Atom state - const [, setShowRelayDetailsDialog] = useAtom(showRelayDetailsDialogAtom) + const [showRelayDetailsDialog, setShowRelayDetailsDialog] = useAtom(showRelayDetailsDialogAtom) const [, setRelaysForDetailsDialog] = useAtom(relaysForDetailsDialogAtom) const [, setRelayDetailsDialogSearch] = useAtom(relayDetailsDialogSearchAtom) const [, setFilteredRelays] = useAtom(filteredRelaysAtom) const [relaySearchOnMap, setRelaySearchOnMap] = useAtom(relaySearchOnMapAtom) + if (showRelayDetailsDialog && !hasOpenedRelayDetailsDialog) setHasOpenedRelayDetailsDialog(true) + if (showFamilySelectionDialog && !hasOpenedFamilySelectionDialog) setHasOpenedFamilySelectionDialog(true) // App context const leafletMap = useMap() @@ -251,15 +257,17 @@ export const LeafletLayers: FunctionComponent = ({relays, reloadSelectedD } }, [filteredRelays, setFilteredRelays, leafletMarkerLayers, relays, statistics, setStatistics, filteredRelaysByFlags.length, settings, relayFamilyMap, leafletMap, relayCountryMap, closeSnackbar, enqueueSnackbar, setSettings, relayLocationHeatmapLayer, countryBordersLayer, aggregatedCoordinatesLayer, relayCountryLayer, relayLayer, relaySelectedFamilyLayer, relayFamilyCoordinatesLayer]) + const closeFamilySelectionDialog = useCallback(() => setShowFamilySelectionDialog(false), []) + return ( }> - - : null} + {hasOpenedFamilySelectionDialog ? setShowFamilySelectionDialog(false), [])} + closeDialog={closeFamilySelectionDialog} reloadSelectedDay={reloadSelectedDay} familyIds={familiesForSelectionDialog} - /> + /> : null} ); };