Skip to content

MBL-2342: Add search filtering by project location#2482

Merged
amy-at-kickstarter merged 7 commits into
mainfrom
feat/adyer/location-filter-2
Jun 24, 2025
Merged

MBL-2342: Add search filtering by project location#2482
amy-at-kickstarter merged 7 commits into
mainfrom
feat/adyer/location-filter-2

Conversation

@amy-at-kickstarter

@amy-at-kickstarter amy-at-kickstarter commented Jun 18, 2025

Copy link
Copy Markdown
Contributor

📲 What

Implement search filtering by location.

location.search.working.mp4

🤔 Why

This is the belated phase 3 of our search filters! Plus filtering by location is very popular.

@amy-at-kickstarter amy-at-kickstarter force-pushed the feat/adyer/location-filter-2 branch 2 times, most recently from 1573761 to 31939b9 Compare June 18, 2025 20:48
@amy-at-kickstarter amy-at-kickstarter changed the base branch from main to feat/adyer/fetch-locations-use-case June 18, 2025 20:49
@amy-at-kickstarter amy-at-kickstarter changed the title Feat/adyer/location filter 2 MBL-2342: Add search filtering by project location Jun 18, 2025
self.searchFilters = searchFilters

let selectedLocationName = searchFilters.location.selectedLocation?.displayableName
_locationSearchText = State(initialValue: selectedLocationName ?? "")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've learned a lot of lessons about initializing state in SwiftUI. You can really only do this particular trick once, when the root view is instantiated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow is _locationSearchText like a private accessor to locationSearchText?

@amy-at-kickstarter amy-at-kickstarter Jun 20, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is a feature of property wrappers (of which State is one). This is what the swift language documentation has to say:

The wrapper defines and manages any underlying storage needed by its wrapped value. The compiler synthesizes storage for the instance of the wrapper type by prefixing the name of the wrapped property with an underscore (_) — for example, the wrapper for someProperty is stored as _someProperty. The synthesized storage for the wrapper has an access control level of private.

So this is the officially supported way to set a State value on init. However, it's pretty tricky to use - if the SwiftUI view is used as a child view anywhere, it may be initialized many times, causing this state to break. This only works here because FilterRootView is initialized once and set as the root view of a UIHostingController.

placement: .navigationBarDrawer(displayMode: .always),
prompt: "FPO: Search by city, state, country..."
)
.searchSuggestions {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent a bunch of time writing my own autosuggestions code, until eventually I realized Apple had already done it for me 🤷‍♀️

selectedLocation: self.$selectedLocation
)
}
.searchable(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't customizable, but I feel like using the system one is very ergonomic and nice.

@amy-at-kickstarter amy-at-kickstarter force-pushed the feat/adyer/location-filter-2 branch from 31939b9 to 03653ab Compare June 18, 2025 20:56
func selectedPercentRaisedBucket(_ bucket: DiscoveryParams.PercentRaisedBucket)

/// Cal this when the user selects a filter location.
func filteredLocation(_: Location?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to name it this everywhere to prevent an overlap with the selectedLocation var.

@amy-at-kickstarter amy-at-kickstarter marked this pull request as ready for review June 18, 2025 20:59

@jovaniks jovaniks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good! Just one suggestion: for any hardcoded strings that don't yet have a corresponding translation, could we wrap them with a TODOS comment or marker so it's easier to track and replace them later?

@ViewBuilder
var locationSection: some View {
FilterSectionButton(
title: "FPO: Location",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have this on translation strings?

.modalHeader(withTitle: Strings.Percentage_raised(), onClose: self.onClose)
case .location:
self.locationModal
.modalHeader(withTitle: "FPO: Location", onClose: self.onClose)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, looks like we want to have this in translated strings

self.selectedLocation = nil
} label: {
self.buttonLabel(
title: "FPO: Anywhere",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

.searchable(
text: self.$searchText,
placement: .navigationBarDrawer(displayMode: .always),
prompt: "FPO: Search by city, state, country..."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to have this included in the translated strings.

var suggestedLocations: [Location]
@Binding var selectedLocation: Location?
var onSearchedForLocations: (String) -> Void
@Binding var searchText: String

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering - since defaultLocations, suggestedLocations, and onSearchedForLocations don't seem to change after initialization, would it make sense to declare them as let instead of var?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, yes, will fix!

self.category.categories = categories
}

internal func update(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since internal is the default access level in Swift, can we remove it here to avoid redundancy?

self.location.defaultLocations = locations
}

internal func update(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

)

if featureSearchFilterByLocation() {
let locationTitle = self.location.selectedLocation?.displayableName ?? "FPO: Location"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we replace this with a translated string?

@amy-at-kickstarter

Copy link
Copy Markdown
Contributor Author

Overall looks good! Just one suggestion: for any hardcoded strings that don't yet have a corresponding translation, could we wrap them with a TODOS comment or marker so it's easier to track and replace them later?

Thanks for the reminder, I'll add TODOs and a ticket number. In this case, "FPO" is "for position only" and is supposed to indicate roughly the same thing, but I do like having TODOs as well.

@amy-at-kickstarter amy-at-kickstarter force-pushed the feat/adyer/fetch-locations-use-case branch from bcb1c3b to e0a6fb0 Compare June 23, 2025 17:29
Base automatically changed from feat/adyer/fetch-locations-use-case to main June 23, 2025 18:25
@amy-at-kickstarter amy-at-kickstarter force-pushed the feat/adyer/location-filter-2 branch from 03653ab to 33952f4 Compare June 23, 2025 18:29

@jovaniks jovaniks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome

@nativeksr

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ Big PR

Generated by 🚫 Danger

@scottkicks scottkicks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay on this one! LGTM

@amy-at-kickstarter amy-at-kickstarter merged commit f3e487d into main Jun 24, 2025
5 checks passed
@amy-at-kickstarter amy-at-kickstarter deleted the feat/adyer/location-filter-2 branch June 24, 2025 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants