@@ -7,56 +7,36 @@ import SWUtils
77/// Экран со списком площадок
88struct ParksListScreen : View {
99 @Environment ( \. dismiss) private var dismiss
10+ @Environment ( \. isNetworkConnected) private var isNetworkConnected
1011 @EnvironmentObject private var defaults : DefaultsService
1112 @EnvironmentObject private var parksManager : ParksManager
12- @State private var parks = [ Park] ( )
13- @State private var isLoading = false
13+ @State private var currentState = CurrentState . initial
1414 /// Площадка для открытия детального экрана
1515 @State private var selectedPark : Park ?
16+ private var client : SWClient { SWClient ( with: defaults) }
1617 let mode : Mode
1718
1819 var body : some View {
1920 ScrollView {
20- LazyVStack ( spacing: 12 ) {
21- ForEach ( parks) { park in
22- Button {
23- switch mode {
24- case let . event( _, callBack) :
25- callBack ( park. id, park. name ?? park. longTitle)
26- dismiss ( )
27- case . usedBy, . added:
28- selectedPark = park
29- }
30- } label: {
31- ParkRowView (
32- imageURL: park. previewImageURL,
33- title: park. longTitle,
34- address: park. address,
35- usersTrainHereText: park. usersTrainHereText
36- )
37- }
38- . accessibilityIdentifier ( " ParkViewCell " )
39- }
40- }
41- . padding ( [ . top, . horizontal] )
21+ contentView
22+ . animation ( . default, value: currentState)
23+ . frame ( maxWidth: . infinity)
4224 }
43- . loadingOverlay ( if: isLoading)
25+ . loadingOverlay ( if: currentState . isLoading)
4426 . background ( Color . swBackground)
4527 . sheet ( item: $selectedPark) { park in
4628 NavigationView {
4729 ParkDetailScreen ( park: park) { deletePark ( id: $0) }
4830 }
4931 . navigationViewStyle ( . stack)
5032 }
51- . onChange ( of: parks) { list in
52- if list. isEmpty {
53- defaults. setUserNeedUpdate ( true )
33+ . onChange ( of: currentState) { newState in
34+ if newState. isReadyAndEmpty {
5435 dismiss ( )
5536 }
5637 }
5738 . task { await askForParks ( ) }
5839 . refreshable {
59- guard mode. canRefreshList else { return }
6040 await askForParks ( refresh: true )
6141 }
6242 . toolbar {
@@ -71,16 +51,45 @@ struct ParksListScreen: View {
7151
7252extension ParksListScreen {
7353 enum Mode {
54+ /// Площадки, где тренируется пользователь
7455 case usedBy( userID: Int )
56+ /// Площадки, где тренируется пользователь, для создания мероприятия
7557 case event( userID: Int , didSelectPark: ( _ id: Int , _ name: String ) -> Void )
58+ /// Площадки, добавленные пользователем
7659 case added( list: [ Park ] )
60+ }
61+ }
62+
63+ extension ParksListScreen {
64+ enum CurrentState : Equatable {
65+ case initial
66+ case loading
67+ case ready( [ Park ] )
68+ case error( ErrorKind )
69+
70+ var isLoading : Bool {
71+ if case . loading = self { true } else { false }
72+ }
73+
74+ /// Нужно ли загружать данные, когда их нет (или для рефреша)
75+ var shouldLoad : Bool {
76+ switch self {
77+ case . initial, . error: true
78+ case let . ready( parks) : parks. isEmpty
79+ case . loading: false
80+ }
81+ }
7782
78- var canRefreshList : Bool {
83+ var isReadyAndNotEmpty : Bool {
7984 switch self {
80- case . added : false
81- case . usedBy , . event : true
85+ case let . ready ( parks ) : !parks . isEmpty
86+ default : false
8287 }
8388 }
89+
90+ var isReadyAndEmpty : Bool {
91+ if case let . ready( parks) = self { parks. isEmpty } else { false }
92+ }
8493 }
8594}
8695
@@ -95,6 +104,39 @@ private extension ParksListScreen.Mode {
95104}
96105
97106private extension ParksListScreen {
107+ @ViewBuilder
108+ var contentView : some View {
109+ switch currentState {
110+ case let . ready( parks) :
111+ LazyVStack ( spacing: 12 ) {
112+ ForEach ( parks) { park in
113+ Button {
114+ switch mode {
115+ case let . event( _, callBack) :
116+ callBack ( park. id, park. name ?? park. longTitle)
117+ dismiss ( )
118+ case . usedBy, . added:
119+ selectedPark = park
120+ }
121+ } label: {
122+ ParkRowView (
123+ imageURL: park. previewImageURL,
124+ title: park. longTitle,
125+ address: park. address,
126+ usersTrainHereText: park. usersTrainHereText
127+ )
128+ }
129+ . accessibilityIdentifier ( " ParkViewCell " )
130+ }
131+ }
132+ . padding ( [ . top, . horizontal] )
133+ case let . error( errorKind) :
134+ CommonErrorView ( errorKind: errorKind)
135+ case . initial, . loading:
136+ EmptyView ( )
137+ }
138+ }
139+
98140 @ViewBuilder
99141 var refreshButtonIfNeeded : some View {
100142 if !DeviceOSVersionChecker. iOS16Available {
@@ -103,47 +145,45 @@ private extension ParksListScreen {
103145 } label: {
104146 Icons . Regular. refresh. view
105147 }
106- . disabled ( isLoading)
148+ . disabled ( currentState . isLoading)
107149 }
108150 }
109151
110152 func askForParks( refresh: Bool = false ) async {
111- if isLoading { return }
112- do {
113- switch mode {
114- case let . usedBy( userID) , let . event( userID, _) :
115- let isMainUser = userID == defaults. mainUserInfo? . id
116- let needUpdate = parks. isEmpty || refresh
117- if isMainUser {
118- if !needUpdate, !defaults. needUpdateUser { return }
119- try await makeList ( for: userID, isMainUser, refresh)
153+ switch mode {
154+ case let . usedBy( userID) , let . event( userID, _) :
155+ guard currentState. shouldLoad || refresh else { return }
156+ guard isNetworkConnected else {
157+ if currentState. isReadyAndNotEmpty {
158+ SWAlert . shared. presentNoConnection ( false )
120159 } else {
121- if !needUpdate { return }
122- try await makeList ( for: userID, isMainUser, refresh)
160+ currentState = . error( . notConnected)
123161 }
124- case let . added( list) :
125- parks = list
162+ return
126163 }
127- } catch {
128- SWAlert . shared. presentDefaultUIKit ( error)
164+ if !refresh {
165+ currentState = . loading
166+ }
167+ do {
168+ let parks = try await SWClient ( with: defaults) . getParksForUser ( userID)
169+ let isMainUser = userID == defaults. mainUserInfo? . id
170+ if isMainUser { defaults. setUserNeedUpdate ( false ) }
171+ currentState = . ready( parks)
172+ } catch {
173+ currentState = . error( . common( message: error. localizedDescription) )
174+ }
175+ case let . added( list) :
176+ currentState = . ready( list)
129177 }
130- isLoading = false
131- }
132-
133- func makeList( for userID: Int , _ isMainUser: Bool , _ isRefreshing: Bool ) async throws {
134- if !isRefreshing { isLoading = true }
135- if isMainUser { defaults. setUserNeedUpdate ( false ) }
136- parks = try await SWClient ( with: defaults) . getParksForUser ( userID)
137178 }
138179
139180 func deletePark( id: Int ) {
140181 selectedPark = nil
141- parks . removeAll ( where : { $0 . id == id } )
182+ guard case let . ready ( parks ) = currentState else { return }
142183 do {
143184 try parksManager. deletePark ( with: id)
144- if !mode. canRefreshList {
145- dismiss ( )
146- }
185+ let updatedParks = parks. filter { $0. id != id }
186+ currentState = . ready( updatedParks)
147187 } catch {
148188 SWAlert . shared. presentDefaultUIKit ( error)
149189 }
0 commit comments