Skip to content

Commit 531197f

Browse files
fix: page load
1 parent 541fcb0 commit 531197f

File tree

5 files changed

+101
-31
lines changed

5 files changed

+101
-31
lines changed

shared-helpers/src/locales/general.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
"account.noClosedApplicationsSimplified": "None of the listings you've applied to have closed.",
4646
"account.noFavorites": "It looks like you haven't favorited any listings yet.",
4747
"account.noLotteryApplications": "None of the listings you've applied to have released lottery results.",
48+
"t.noMatchingListings": "No matching listings",
4849
"t.noVisibleListings": "No visible listings",
50+
"t.tryChangingArea": "Try adjusting your search area.",
51+
"t.tryRemovingFilters": "Try removing some of your filters.",
4952
"search.filters": "Filters",
5053
"search.totalResults": "Total results",
5154
"t.recenterMap": "Recenter",

sites/public/src/components/browse/map/ListingsCombined.module.scss

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,12 @@
105105
CSS module renaming and use these exact class names */
106106
[class*="loading-overlay"] {
107107
padding: 0;
108-
min-height: var(--seeds-s40);
109-
margin-bottom: var(--seeds-s4);
108+
min-height: var(--seeds-s32);
110109
z-index: 1;
111110
}
112111

113112
[class*="loading-overlay__spinner"] {
114-
top: var(--seeds-s20);
113+
top: var(--seeds-s16);
115114
color: var(--seeds-color-primary);
116115
}
117116
}
@@ -138,6 +137,10 @@
138137

139138
.listings-list-container {
140139
border-bottom: 1px solid var(--seeds-color-gray-450);
141-
margin-bottom: var(--seeds-s4);
142140
padding: var(--seeds-s4);
143141
}
142+
143+
.listings-list-container-empty {
144+
border-bottom: 0;
145+
min-height: var(--seeds-s32);
146+
}

sites/public/src/components/browse/map/ListingsList.tsx

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import * as React from "react"
2-
import {
3-
Jurisdiction,
4-
Listing,
5-
ListingMapMarker,
6-
} from "@bloom-housing/shared-helpers/src/types/backend-swagger"
2+
import { Listing, ListingMapMarker } from "@bloom-housing/shared-helpers/src/types/backend-swagger"
73
import { Button, Heading } from "@bloom-housing/ui-seeds"
8-
// import { ZeroListingsItem } from "@bloom-housing/doorway-ui-components"
94
import { LoadingOverlay, t, InfoCard } from "@bloom-housing/ui-components"
105
import { getMapListings } from "../../../lib/helpers"
116
import { Pagination } from "./Pagination"
@@ -28,21 +23,36 @@ const ListingsList = (props: ListingsListProps) => {
2823
<Heading className={"sr-only"} priority={2}>
2924
{t("t.listingsList")}
3025
</Heading>
31-
{props.listings.length > 0 || props.loading ? (
32-
<div className={styles["listings-list-container"]}>{getMapListings(props.listings)}</div>
33-
) : (
34-
// Map TODO: Create zero listings component
35-
// <ZeroListingsItem
36-
// title={moreMarkersOnMap ? t("t.noVisibleListings") : t("t.noMatchingListings")}
37-
// description={moreMarkersOnMap ? t("t.tryChangingArea") : t("t.tryRemovingFilters")}
38-
// />
39-
<>{moreMarkersOnMap ? t("t.noVisibleListings") : t("t.noMatchingListings")}</>
40-
)}
26+
<div
27+
className={`${styles["listings-list-container"]} ${
28+
props.listings.length === 0 && styles["listings-list-container-empty"]
29+
}`}
30+
>
31+
{props.listings.length > 0 || props.loading ? (
32+
getMapListings(props.listings)
33+
) : (
34+
<>
35+
{moreMarkersOnMap ? (
36+
<>
37+
<Heading priority={2} size={"xl"}>
38+
{t("t.noVisibleListings")}
39+
</Heading>
40+
<div>{t("t.tryChangingArea")}</div>
41+
</>
42+
) : (
43+
<>
44+
<Heading priority={2} size={"xl"}>
45+
{t("t.noMatchingListings")}
46+
</Heading>
47+
<div>{t("t.tryRemovingFilters")}</div>
48+
</>
49+
)}
50+
</>
51+
)}
52+
</div>
4153
</div>
4254
)
4355

44-
console.log(props.loading)
45-
4656
const infoCards = (
4757
<div className={styles["info-cards-container"]}>
4858
{process.env.notificationsSignUpUrl && (

sites/public/src/components/browse/map/ListingsSearchCombined.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect, useContext } from "react"
1+
import React, { useState, useEffect, useContext, useRef } from "react"
22
import { useMap } from "@vis.gl/react-google-maps"
33
import { ListingList, pushGtmEvent, AuthContext } from "@bloom-housing/shared-helpers"
44
import { t } from "@bloom-housing/ui-components"
@@ -55,6 +55,7 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
5555
const [isDesktop, setIsDesktop] = useState(undefined)
5656
const [isLoading, setIsLoading] = useState(true)
5757
const [isFirstBoundsLoad, setIsFirstBoundsLoad] = useState<boolean>(true)
58+
const hasCompletedInitialListingsCallRef = useRef(false)
5859

5960
const [searchFilter] = useState(
6061
parseSearchString(props.searchString, {
@@ -104,6 +105,10 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
104105
)
105106

106107
if (visibleMarkers?.length === 0 && !changingFilter) {
108+
if (!hasCompletedInitialListingsCallRef.current) {
109+
return
110+
}
111+
107112
setSearchResults({
108113
listings: [],
109114
markers: searchResults.markers,
@@ -153,6 +158,7 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
153158
props.jurisdictionIds,
154159
drawerFilters
155160
)
161+
hasCompletedInitialListingsCallRef.current = true
156162
newListings = result.items
157163
newMeta = result.meta
158164
}
@@ -256,7 +262,6 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
256262

257263
// Re-search when the map's visible markers are changed
258264
useEffect(() => {
259-
console.log("visible markers changed use effect")
260265
async function searchListings() {
261266
await search(1)
262267
}
@@ -274,8 +279,9 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
274279
setCurrentMarkers(visibleMarkers)
275280
void searchListings()
276281
} else {
277-
console.log("set loading, to false they are the same")
278-
setIsLoading(false)
282+
if (hasCompletedInitialListingsCallRef.current) {
283+
setIsLoading(false)
284+
}
279285
}
280286
// eslint-disable-next-line react-hooks/exhaustive-deps
281287
}, [visibleMarkers])
@@ -302,7 +308,9 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) {
302308
}
303309

304310
const onDrawerSubmit = (data: FilterData) => {
305-
const backendFilters = encodeFilterDataToBackendFilters(data)
311+
const backendFilters = encodeFilterDataToBackendFilters(data).filter(
312+
(filter) => filter.name !== ""
313+
)
306314
setFilterState(data)
307315
setDrawerFilters(backendFilters)
308316
setFilterCount(backendFilters.length)

sites/public/src/components/browse/map/MapClusterer.tsx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export const MapClusterer = ({
114114
const mapRef = useRef(map)
115115
const mapMarkersRef = useRef(mapMarkers)
116116
const visibleMarkersRef = useRef(visibleMarkers)
117+
const hasSeenNonEmptyVisibleMarkersRef = useRef(false)
117118
const isFirstBoundsLoadRef = useRef(isFirstBoundsLoad)
118119
const isDesktopRef = useRef(isDesktop)
119120

@@ -133,10 +134,26 @@ export const MapClusterer = ({
133134
// Compute markers currently inside map bounds using the latest map + markers refs
134135
const getVisibleMarkersInBounds = useCallback(() => {
135136
const currentMap = mapRef.current
136-
if (!currentMap) return []
137+
if (!currentMap) return visibleMarkersRef.current ?? []
137138

138139
const bounds = currentMap.getBounds()
139140
const currentMapMarkers = mapMarkersRef.current
141+
142+
// On slower connections/renders, bounds can be temporarily undefined while markers already exist
143+
// During initial load (or before we've ever set visible markers), treat all fetched
144+
// markers as visible until bounds are ready to avoid showing "no results" state prematurely
145+
if (!bounds) {
146+
if (
147+
(isFirstBoundsLoadRef.current || (visibleMarkersRef.current?.length ?? 0) === 0) &&
148+
(currentMapMarkers?.length ?? 0) > 0
149+
) {
150+
return currentMapMarkers
151+
}
152+
return visibleMarkersRef.current ?? []
153+
}
154+
155+
if (!currentMapMarkers) return visibleMarkersRef.current ?? []
156+
140157
return currentMapMarkers?.filter((marker) => bounds?.contains(marker.coordinate)) ?? []
141158
}, [])
142159

@@ -151,16 +168,38 @@ export const MapClusterer = ({
151168

152169
const resetVisibleMarkers = useCallback(() => {
153170
const newVisibleMarkers = getVisibleMarkersInBounds()
171+
const currentMapMarkers = mapMarkersRef.current ?? []
172+
173+
if (newVisibleMarkers.length > 0) {
174+
hasSeenNonEmptyVisibleMarkersRef.current = true
175+
}
176+
154177
// During first desktop cycle, avoid triggering marker-driven search
155178
if (isFirstBoundsLoadRef.current && isDesktopRef.current) return
156179

180+
// On slow first load, fitBounds can complete before map bounds fully settle
181+
// If markers exist but the very first computed visible set is empty, skip this
182+
// state to avoid false "no visible listings"
183+
if (
184+
isDesktopRef.current &&
185+
!hasSeenNonEmptyVisibleMarkersRef.current &&
186+
currentMapMarkers.length > 0 &&
187+
newVisibleMarkers.length === 0
188+
) {
189+
setIsLoading(false)
190+
return
191+
}
192+
157193
// When the computed visible set is identical, do not retrigger loading
158194
const nextSignature = markerSetSignature(newVisibleMarkers)
159195
const currentSignature = markerSetSignature(visibleMarkersRef.current)
160-
if (nextSignature === currentSignature) return
196+
if (nextSignature === currentSignature) {
197+
setIsLoading(false)
198+
return
199+
}
161200

162201
setVisibleMarkers(newVisibleMarkers)
163-
}, [getVisibleMarkersInBounds, markerSetSignature, setVisibleMarkers])
202+
}, [getVisibleMarkersInBounds, markerSetSignature, setIsLoading, setVisibleMarkers])
164203

165204
const debouncedResetVisibleMarkers = useMemo(
166205
() => debounce(resetVisibleMarkers, 800),
@@ -173,7 +212,6 @@ export const MapClusterer = ({
173212
// After pan/zoom settles, refresh visible markers
174213
// First load runs immediately, then later interactions are debounced
175214
const idleListener = map.addListener("idle", () => {
176-
console.log("idle listener")
177215
if (isFirstBoundsLoad) {
178216
resetVisibleMarkers()
179217
return
@@ -224,6 +262,14 @@ export const MapClusterer = ({
224262
// eslint-disable-next-line react-hooks/exhaustive-deps
225263
}, [mapMarkers, map])
226264

265+
// Once first fitBounds cycle completes, trigger one reset so desktop listings can
266+
// search immediately without requiring an additional drag
267+
useEffect(() => {
268+
if (!map) return
269+
if (isFirstBoundsLoad) return
270+
resetVisibleMarkers()
271+
}, [isFirstBoundsLoad, map, resetVisibleMarkers])
272+
227273
const fetchInfoWindow = async (listingId: string) => {
228274
try {
229275
const response = await listingsService.retrieve({

0 commit comments

Comments
 (0)