Skip to content

Commit 014806a

Browse files
authored
Add INFO view (#489)
1 parent 61ad9f4 commit 014806a

27 files changed

+487
-225
lines changed

App/iOS App/App.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
2727
reducer: AppFeature()
2828
)
2929
lazy var viewStore = ViewStore(
30-
self.store.scope(state: { _ in () }),
30+
self.store.scope(state: { _ in () }, action: { $0 }),
3131
removeDuplicates: ==
3232
)
3333

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
Changelog for Critical Maps iOS
44

5+
# [4.3.0] - 2023-07-21
6+
7+
### Added
8+
9+
- Info view with next update countdown and riders count view
10+
11+
### Updated
12+
13+
- Send location on a timly base
14+
15+
### Fixed
16+
17+
- Store user settings failure fixed
18+
19+
520
# [4.2.1] - 2023-06-05
621

722
### Fixed

CriticalMapsKit/Sources/AppFeature/AppFeatureCore.swift

Lines changed: 69 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import MapFeature
1111
import MapKit
1212
import MastodonFeedFeature
1313
import NextRideFeature
14-
import PathMonitorClient
1514
import SettingsFeature
1615
import SharedDependencies
1716
import SharedModels
@@ -33,7 +32,6 @@ public struct AppFeature: ReducerProtocol {
3332
@Dependency(\.locationManager) public var locationManager
3433
@Dependency(\.uiApplicationClient) public var uiApplicationClient
3534
@Dependency(\.setUserInterfaceStyle) public var setUserInterfaceStyle
36-
@Dependency(\.pathMonitorClient) public var pathMonitorClient
3735
@Dependency(\.isNetworkAvailable) public var isNetworkAvailable
3836

3937
// MARK: State
@@ -66,17 +64,34 @@ public struct AppFeature: ReducerProtocol {
6664

6765
public var riderLocations: TaskResult<[Rider]>?
6866
public var didResolveInitialLocation = false
67+
public var isRequestingRiderLocations = false
6968

7069
// Children states
7170
public var mapFeatureState = MapFeature.State(
7271
riders: [],
7372
userTrackingMode: UserTrackingFeature.State(userTrackingMode: .follow)
7473
)
74+
75+
public var timerProgress: Double {
76+
let progress = Double(requestTimer.secondsElapsed) / 60
77+
return progress
78+
}
79+
public var sendLocation: Bool {
80+
requestTimer.secondsElapsed == 30
81+
}
82+
public var timerValue: String {
83+
let progress = 60 - requestTimer.secondsElapsed
84+
return String(progress)
85+
}
86+
public var ridersCount: String {
87+
let count = mapFeatureState.visibleRidersCount ?? 0
88+
return NumberFormatter.riderCountFormatter.string(from: .init(value: count)) ?? ""
89+
}
90+
7591
public var socialState = SocialFeature.State()
7692
public var settingsState = SettingsFeature.State(userSettings: .init())
7793
public var nextRideState = NextRideFeature.State()
7894
public var requestTimer = RequestTimer.State()
79-
public var connectionObserverState = NetworkConnectionObserver.State()
8095

8196
// Navigation
8297
public var route: AppRoute?
@@ -129,20 +144,12 @@ public struct AppFeature: ReducerProtocol {
129144
case requestTimer(RequestTimer.Action)
130145
case settings(SettingsFeature.Action)
131146
case social(SocialFeature.Action)
132-
case connectionObserver(NetworkConnectionObserver.Action)
133147
}
134148

135149
// MARK: Reducer
136-
137-
struct ObserveConnectionIdentifier: Hashable {}
138-
139150
public var body: some ReducerProtocol<State, Action> {
140151
BindingReducer()
141152

142-
Scope(state: \.connectionObserverState, action: /AppFeature.Action.connectionObserver) {
143-
NetworkConnectionObserver()
144-
}
145-
146153
Scope(state: \.requestTimer, action: /AppFeature.Action.requestTimer) {
147154
RequestTimer()
148155
}
@@ -190,6 +197,15 @@ public struct AppFeature: ReducerProtocol {
190197

191198
case .onAppear:
192199
var effects: [EffectTask<Action>] = [
200+
EffectTask(value: .map(.onAppear)),
201+
EffectTask(value: .requestTimer(.startTimer)),
202+
.task {
203+
await .userSettingsLoaded(
204+
TaskResult {
205+
try await fileClient.loadUserSettings()
206+
}
207+
)
208+
},
193209
.run { send in
194210
await withThrowingTaskGroup(of: Void.self) { group in
195211
group.addTask {
@@ -199,16 +215,7 @@ public struct AppFeature: ReducerProtocol {
199215
await send(.fetchLocations)
200216
}
201217
}
202-
},
203-
.task {
204-
await .userSettingsLoaded(
205-
TaskResult {
206-
try await fileClient.loadUserSettings()
207-
}
208-
)
209-
},
210-
EffectTask(value: .map(.onAppear)),
211-
EffectTask(value: .requestTimer(.startTimer))
218+
}
212219
]
213220
if !userDefaultsClient.didShowObservationModePrompt {
214221
effects.append(
@@ -222,9 +229,10 @@ public struct AppFeature: ReducerProtocol {
222229
return .merge(effects)
223230

224231
case .onDisappear:
225-
return EffectTask.cancel(id: ObserveConnectionIdentifier())
232+
return .none
226233

227234
case .fetchLocations:
235+
state.isRequestingRiderLocations = true
228236
return .task {
229237
await .fetchLocationsResponse(
230238
TaskResult {
@@ -268,11 +276,13 @@ public struct AppFeature: ReducerProtocol {
268276
return .none
269277

270278
case let .fetchLocationsResponse(.success(response)):
279+
state.isRequestingRiderLocations = false
271280
state.riderLocations = .success(response)
272281
state.mapFeatureState.riderLocations = response
273282
return .none
274283

275284
case let .fetchLocationsResponse(.failure(error)):
285+
state.isRequestingRiderLocations = false
276286
logger.info("FetchLocation failed: \(error)")
277287
state.riderLocations = .failure(error)
278288
return .none
@@ -311,29 +321,8 @@ public struct AppFeature: ReducerProtocol {
311321
}
312322

313323
case .locationManager(.didUpdateLocations):
314-
let isInitialLocation = state.nextRideState.userLocation == nil
315-
316-
// sync with nextRideState
317324
state.nextRideState.userLocation = state.mapFeatureState.location?.coordinate
318-
319-
if
320-
let coordinate = state.mapFeatureState.location?.coordinate,
321-
state.settingsState.rideEventSettings.isEnabled,
322-
isInitialLocation
323-
{
324-
return .run { send in
325-
await withThrowingTaskGroup(of: Void.self) { group in
326-
group.addTask {
327-
await send(.postLocation)
328-
}
329-
group.addTask {
330-
await send(.nextRide(.getNextRide(coordinate)))
331-
}
332-
}
333-
}
334-
} else {
335-
return EffectTask(value: .postLocation)
336-
}
325+
return .none
337326

338327
default:
339328
return .none
@@ -359,8 +348,17 @@ public struct AppFeature: ReducerProtocol {
359348
let userSettings = (try? result.value) ?? UserSettings()
360349
state.settingsState = .init(userSettings: userSettings)
361350
state.nextRideState.rideEventSettings = userSettings.rideEventSettings
351+
362352
let style = state.settingsState.appearanceSettings.colorScheme.userInterfaceStyle
353+
let coordinate = state.mapFeatureState.location?.coordinate
354+
let isRideEventsEnabled = state.settingsState.rideEventSettings.isEnabled
355+
363356
return .merge(
357+
.run { send in
358+
if isRideEventsEnabled, let coordinate {
359+
await send(.nextRide(.getNextRide(coordinate)))
360+
}
361+
},
364362
.fireAndForget {
365363
await setUserInterfaceStyle(style)
366364
}
@@ -386,19 +384,27 @@ public struct AppFeature: ReducerProtocol {
386384
case let .requestTimer(timerAction):
387385
switch timerAction {
388386
case .timerTicked:
389-
return .run { [isChatPresented = state.isChatViewPresented, isPrentingSubView = state.route != nil] send in
390-
await withThrowingTaskGroup(of: Void.self) { group in
391-
if !isPrentingSubView {
392-
group.addTask {
393-
await send(.fetchLocations)
387+
if state.requestTimer.secondsElapsed == 60 {
388+
state.requestTimer.secondsElapsed = 0
389+
390+
return .run { [isChatPresented = state.isChatViewPresented, isPrentingSubView = state.route != nil] send in
391+
await withThrowingTaskGroup(of: Void.self) { group in
392+
if !isPrentingSubView {
393+
group.addTask {
394+
await send(.fetchLocations)
395+
}
394396
}
395-
}
396-
if isChatPresented {
397-
group.addTask {
398-
await send(.fetchChatMessages)
397+
if isChatPresented {
398+
group.addTask {
399+
await send(.fetchChatMessages)
400+
}
399401
}
400402
}
401403
}
404+
} else if state.sendLocation {
405+
return EffectTask(value: .postLocation)
406+
} else {
407+
return .none
402408
}
403409

404410
default:
@@ -447,10 +453,7 @@ public struct AppFeature: ReducerProtocol {
447453
default:
448454
return .none
449455
}
450-
451-
case .connectionObserver:
452-
return .none
453-
456+
454457
case .binding:
455458
return .none
456459
}
@@ -501,3 +504,12 @@ public extension AlertState where Action == AppFeature.Action {
501504
}
502505

503506
public typealias ReducerBuilderOf<R: ReducerProtocol> = ReducerBuilder<R.State, R.Action>
507+
508+
extension NumberFormatter {
509+
static let riderCountFormatter: NumberFormatter = {
510+
let formatter = NumberFormatter()
511+
formatter.numberStyle = .decimal
512+
formatter.groupingSeparator = "."
513+
return formatter
514+
}()
515+
}

CriticalMapsKit/Sources/AppFeature/AppNavigationView.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,18 @@ import Styleguide
1010
import SwiftUI
1111

1212
public struct AppNavigationView: View {
13-
public typealias State = AppFeature.State
14-
public typealias Action = AppFeature.Action
13+
let store: StoreOf<AppFeature>
1514

16-
let store: Store<State, Action>
17-
@ObservedObject var viewStore: ViewStore<State, Action>
15+
@ObservedObject var viewStore: ViewStoreOf<AppFeature>
1816
@Environment(\.colorScheme) var colorScheme
1917
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
2018
@Environment(\.colorSchemeContrast) var colorSchemeContrast
2119

2220
let minHeight: CGFloat = 56
2321

24-
public init(store: Store<State, Action>) {
22+
public init(store: StoreOf<AppFeature>) {
2523
self.store = store
26-
viewStore = ViewStore(store)
24+
viewStore = ViewStore(store, observe: { $0 })
2725
}
2826

2927
public var body: some View {

0 commit comments

Comments
 (0)