Skip to content

Commit c4348b7

Browse files
authored
Фильтруем список площадок (#318)
- При определении местоположения пользователя выполняем поиск города в списке сохраненных городов в памяти приложения - Если нашли город, то сразу применяем его для фильтра списка площадок на экране со списком площадок - Сохраняем в `UserDefaults` выбранный город для фильтра списка площадок
1 parent eebbb3b commit c4348b7

File tree

4 files changed

+76
-10
lines changed

4 files changed

+76
-10
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
import SWModels
3+
4+
extension City: @retroactive RawRepresentable {
5+
public var rawValue: String { name }
6+
7+
public init?(rawValue: String) {
8+
do {
9+
self = try SWAddress().findCity(with: rawValue)
10+
} catch {
11+
return nil
12+
}
13+
}
14+
}

SwiftUI-WorkoutApp/Screens/Parks/Map/ParksMapScreen+ViewModel.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import MapKit.MKGeometry
22
import MapView991
33
import OSLog
4+
import SwiftUI // для использования @AppStorage
45
import SWModels
56

67
extension ParksMapScreen {
@@ -13,6 +14,17 @@ extension ParksMapScreen {
1314
private let manager = CLLocationManager()
1415
@Published private(set) var locationErrorMessage = ""
1516
@Published private(set) var addressString = ""
17+
/// Город для фильтра списка площадок
18+
@AppStorage("selectedCityFilter") private(set) var selectedCity: City?
19+
var cityFilterButtonTitle: String {
20+
if let selectedCity {
21+
selectedCity.name
22+
} else {
23+
"Выбери город"
24+
}
25+
}
26+
27+
var canClearCityFilter: Bool { selectedCity != nil }
1628
@Published private(set) var region = MKCoordinateRegion()
1729
@Published private(set) var ignoreUserLocation = false
1830
/// Координаты города в профиле авторизованного пользователя
@@ -28,6 +40,10 @@ extension ParksMapScreen {
2840
func updateUserCountryAndCity(with info: UserResponse?) {
2941
userCoordinates = SWAddress(info?.countryID, info?.cityID)?.coordinates ?? (0, 0)
3042
}
43+
44+
func updateSelectedCity(_ newCity: City?) {
45+
selectedCity = newCity
46+
}
3147
}
3248
}
3349

@@ -123,7 +139,23 @@ private extension ParksMapScreen.ViewModel {
123139
}()
124140
if let fullAddress, fullAddress != addressString {
125141
addressString = fullAddress
126-
logger.debug("Местоположение пользователя: \(fullAddress, privacy: .public)")
142+
logger.debug("Местоположение пользователя: \(fullAddress)")
143+
}
144+
guard selectedCity == nil else {
145+
let cityName = selectedCity!.name
146+
logger.debug("Город для фильтра площадок уже определен: \(cityName)")
147+
return
148+
}
149+
guard let cityName = placemark.locality else {
150+
logger.error("Не удалось определить название города: \(placemark.debugDescription, privacy: .public)")
151+
return
152+
}
153+
do {
154+
let storedCity = try SWAddress().findCity(with: cityName)
155+
logger.debug("Город пользователя для фильтра списка площадок: \(storedCity.name)")
156+
selectedCity = storedCity
157+
} catch {
158+
logger.error("Не удалось найти город \(cityName) в списке сохраненных городов, ошибка: \(error.localizedDescription)")
127159
}
128160
}
129161
}

SwiftUI-WorkoutApp/Screens/Parks/Map/ParksMapScreen.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ struct ParksMapScreen: View {
1414
@State private var isLoading = false
1515
@State private var sheetItem: SheetItem?
1616
@State private var filter = ParkFilterScreen.Model()
17-
/// Город для фильтра списка площадок
18-
@State private var selectedCity: City?
1917
/// Отфильтрованные площадки для вкладки "Карта"
2018
private var filteredMapParks: [Park] {
2119
parksManager.fullList.filter { park in
@@ -26,7 +24,7 @@ struct ParksMapScreen: View {
2624

2725
/// Отфильтрованные по выбранному городу площадки для вкладки "Список"
2826
private var filteredListParks: [Park] {
29-
if let selectedCity {
27+
if let selectedCity = viewModel.selectedCity {
3028
filteredMapParks.filter { $0.cityID == Int(selectedCity.id) }
3129
} else {
3230
filteredMapParks
@@ -132,10 +130,10 @@ private extension ParksMapScreen {
132130
VStack(spacing: 16) {
133131
if let storedCities = try? SWAddress().cities() {
134132
SWTextFieldSearchButton(
135-
selectedCity == nil ? "Выбери город" : "\(selectedCity!.name)",
136-
showClearButton: selectedCity != nil,
133+
.init(viewModel.cityFilterButtonTitle),
134+
showClearButton: viewModel.canClearCityFilter,
137135
mainAction: { sheetItem = .searchCity(storedCities) },
138-
clearAction: { selectedCity = nil }
136+
clearAction: { viewModel.updateSelectedCity(nil) }
139137
)
140138
.padding(.horizontal)
141139
}
@@ -155,7 +153,7 @@ private extension ParksMapScreen {
155153
.accessibilityIdentifier("ParkViewCell")
156154
}
157155
}
158-
.padding(.horizontal)
156+
.padding([.horizontal, .bottom])
159157
}
160158
}
161159
case .map:
@@ -267,9 +265,10 @@ private extension ParksMapScreen {
267265
ItemListScreen(
268266
mode: .city,
269267
allItems: storedCities.map(\.name),
270-
selectedItem: selectedCity?.name ?? "",
268+
selectedItem: viewModel.selectedCity?.name ?? "",
271269
didSelectItem: { cityName in
272-
selectedCity = storedCities.first(where: { $0.name == cityName })
270+
let newCity = storedCities.first(where: { $0.name == cityName })
271+
viewModel.updateSelectedCity(newCity)
273272
}
274273
)
275274
.toolbar {

SwiftUI-WorkoutApp/Services/SWAddress.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,27 @@ extension SWAddress {
114114
func cities() throws -> [City] {
115115
try countries().flatMap(\.cities)
116116
}
117+
118+
func findCity(with name: String) throws -> City {
119+
let storedCities = try cities()
120+
guard let storedCity = storedCities.first(where: { $0.name.lowercased() == name.lowercased() }) else {
121+
throw AddressError.failedToFindCityByName(name)
122+
}
123+
return storedCity
124+
}
125+
}
126+
127+
extension SWAddress {
128+
enum AddressError: Error, LocalizedError {
129+
case failedToFindCityByName(String)
130+
131+
var errorDescription: String? {
132+
switch self {
133+
case let .failedToFindCityByName(name):
134+
"Не удалось найти город \(name)"
135+
}
136+
}
137+
}
117138
}
118139

119140
private extension SWAddress {

0 commit comments

Comments
 (0)