Skip to content

Commit 2eb2e12

Browse files
committed
Add saved search functionality: implement applySavedSearch method for loading bookmarked filters and location.
1 parent 4f36da5 commit 2eb2e12

4 files changed

Lines changed: 66 additions & 6 deletions

File tree

web/components/filters/search.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {QuestionMarkCircleIcon} from '@heroicons/react/24/outline'
22
import {XMarkIcon} from '@heroicons/react/24/solid'
33
import clsx from 'clsx'
44
import {DisplayUser} from 'common/api/user-types'
5-
import {FilterFields} from 'common/filters'
5+
import {FilterFields, OriginLocation} from 'common/filters'
66
import {Profile} from 'common/profiles/profile'
77
import {DisplayOptions} from 'common/profiles-rendering'
88
import {debounce as debounceFn} from 'lodash'
@@ -39,6 +39,10 @@ export const Search = forwardRef<
3939
locationFilterProps: LocationFilterProps
4040
bookmarkedSearches: BookmarkedSearchesType[]
4141
refreshBookmarkedSearches: () => void
42+
applySavedSearch: (
43+
filters: Partial<FilterFields>,
44+
location: {location?: OriginLocation | null; radius?: number} | null,
45+
) => void
4246
profileCount: number | undefined
4347
activeFilterCount?: number
4448
openFilters?: () => void
@@ -62,6 +66,7 @@ export const Search = forwardRef<
6266
filters,
6367
bookmarkedSearches,
6468
refreshBookmarkedSearches,
69+
applySavedSearch,
6570
starredUsers,
6671
refreshStars,
6772
profileCount,
@@ -279,6 +284,7 @@ export const Search = forwardRef<
279284
bookmarkedSearches={bookmarkedSearches}
280285
open={openBookmarks}
281286
setOpen={setOpenBookmarks}
287+
applySavedSearch={applySavedSearch}
282288
/>
283289

284290
<BookmarkStarButton

web/components/filters/use-filters.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {debug} from 'common/logger'
55
import {kmToMiles} from 'common/measurement-utils'
66
import {Profile, ProfileRow} from 'common/profiles/profile'
77
import {removeNullOrUndefinedProps} from 'common/util/object'
8-
import {debounce, isEqual, mapValues, omitBy} from 'lodash'
8+
import {debounce, isEqual, mapValues, omit, omitBy} from 'lodash'
99
import {useCallback, useEffect, useRef} from 'react'
1010
import {useIsLooking} from 'web/hooks/use-is-looking'
1111
import {useMeasurementSystem} from 'web/hooks/use-measurement-system'
@@ -169,6 +169,26 @@ export const useFilters = (you: Profile | undefined, fromSignup?: boolean) => {
169169
setRaisedInLocation(undefined)
170170
}
171171

172+
// Loads a bookmarked search back into the grid. Like applyLookingForFilters this *replaces* the
173+
// current search rather than merging into it — a saved search stands for the whole set of criteria
174+
// that was saved, so leftovers from the current one would silently narrow it.
175+
// Same single-setFilters-call constraint as applyLookingForFilters (see the comment there).
176+
// The saved row only stores the "lives near" location object, not the "grew up near" one, so the
177+
// raised_in_* coordinates are dropped instead of being applied as an unlabeled filter that the
178+
// raised-in effect would clear on the next mount anyway.
179+
const applySavedSearch = (
180+
savedFilters: Partial<FilterFields>,
181+
savedLocation: {location?: OriginLocation | null; radius?: number} | null | undefined,
182+
) => {
183+
setFilters({
184+
...baseFilters,
185+
...omit(savedFilters, ['raised_in_lat', 'raised_in_lon', 'raised_in_radius']),
186+
})
187+
setRaisedInLocation(undefined)
188+
setLocation(savedLocation?.location ?? undefined)
189+
if (savedLocation?.radius) setRadius(savedLocation.radius)
190+
}
191+
172192
const setLookingForFilters = (checked: boolean) => {
173193
applyLookingForFilters(checked ? you : undefined)
174194
}
@@ -197,6 +217,7 @@ export const useFilters = (you: Profile | undefined, fromSignup?: boolean) => {
197217
clearFilters,
198218
setLookingForFilters,
199219
applyLookingForFilters,
220+
applySavedSearch,
200221
isLookingForFilters,
201222
locationFilterProps,
202223
raisedInLocationFilterProps,

web/components/profiles/profiles-home.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export function ProfilesHome() {
7171
clearFilters,
7272
setLookingForFilters,
7373
applyLookingForFilters,
74+
applySavedSearch,
7475
isLookingForFilters,
7576
locationFilterProps,
7677
raisedInLocationFilterProps,
@@ -378,6 +379,7 @@ export function ProfilesHome() {
378379
locationFilterProps={locationFilterProps}
379380
bookmarkedSearches={bookmarkedSearches}
380381
refreshBookmarkedSearches={refreshBookmarkedSearches}
382+
applySavedSearch={applySavedSearch}
381383
profileCount={profileCount}
382384
activeFilterCount={activeFilterCount}
383385
filtersElement={filtersElement}

web/components/searches/button.tsx

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import {BookmarkIcon, XMarkIcon} from '@heroicons/react/24/outline'
1+
import {BookmarkIcon, MagnifyingGlassIcon, XMarkIcon} from '@heroicons/react/24/outline'
22
import clsx from 'clsx'
33
import {DisplayUser} from 'common/api/user-types'
4-
import {FilterFields} from 'common/filters'
4+
import {FilterFields, OriginLocation} from 'common/filters'
55
import {formatFilters, locationType} from 'common/filters-format'
66
import {User} from 'common/user'
77
import {Users} from 'lucide-react'
@@ -19,15 +19,20 @@ import {useMeasurementSystem} from 'web/hooks/use-measurement-system'
1919
import {useUser} from 'web/hooks/use-user'
2020
import {api} from 'web/lib/api'
2121
import {useT} from 'web/lib/locale'
22+
import {track} from 'web/lib/service/analytics'
2223
import {deleteBookmarkedSearch} from 'web/lib/supabase/searches'
2324

2425
export function BookmarkSearchButton(props: {
2526
bookmarkedSearches: BookmarkedSearchesType[]
2627
refreshBookmarkedSearches: () => void
2728
open: boolean
2829
setOpen: (checked: boolean) => void
30+
applySavedSearch?: (
31+
filters: Partial<FilterFields>,
32+
location: {location?: OriginLocation | null; radius?: number} | null,
33+
) => void
2934
}) {
30-
const {bookmarkedSearches, refreshBookmarkedSearches, open, setOpen} = props
35+
const {bookmarkedSearches, refreshBookmarkedSearches, open, setOpen, applySavedSearch} = props
3136
const user = useUser()
3237

3338
const t = useT()
@@ -51,6 +56,7 @@ export function BookmarkSearchButton(props: {
5156
user={user}
5257
bookmarkedSearches={bookmarkedSearches}
5358
refreshBookmarkedSearches={refreshBookmarkedSearches}
59+
applySavedSearch={applySavedSearch}
5460
/>
5561
</>
5662
)
@@ -74,8 +80,12 @@ function ButtonModal(props: {
7480
user: User
7581
bookmarkedSearches: BookmarkedSearchesType[]
7682
refreshBookmarkedSearches: () => void
83+
applySavedSearch?: (
84+
filters: Partial<FilterFields>,
85+
location: {location?: OriginLocation | null; radius?: number} | null,
86+
) => void
7787
}) {
78-
const {open, setOpen, bookmarkedSearches, refreshBookmarkedSearches} = props
88+
const {open, setOpen, bookmarkedSearches, refreshBookmarkedSearches, applySavedSearch} = props
7989
const t = useT()
8090
const choicesIdsToLabels = useChoicesContext()
8191
const {measurementSystem} = useMeasurementSystem()
@@ -116,6 +126,27 @@ function ButtonModal(props: {
116126
)?.join(' • ')}
117127
</div>
118128
</div>
129+
{applySavedSearch && (
130+
<button
131+
data-testid="apply-saved-search"
132+
onClick={() => {
133+
applySavedSearch(
134+
(search.search_filters ?? {}) as Partial<FilterFields>,
135+
search.location as {
136+
location?: OriginLocation | null
137+
radius?: number
138+
} | null,
139+
)
140+
track('bookmarked_searches apply', {id: search.id})
141+
setOpen(false)
142+
}}
143+
className="inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-canvas-300 bg-canvas-0 px-2.5 h-8 text-xs font-medium text-ink-600 hover:border-primary-400 hover:bg-primary-50 hover:text-primary-700 transition-all focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-1"
144+
aria-label={t('saved_searches.apply', 'Apply this search')}
145+
>
146+
<MagnifyingGlassIcon className="h-4 w-4" />
147+
{t('saved_searches.apply_short', 'Apply')}
148+
</button>
149+
)}
119150
<button
120151
onClick={async () => {
121152
await deleteBookmarkedSearch(search.id)

0 commit comments

Comments
 (0)