Skip to content

Commit

Permalink
update settings persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
mltbnz committed Jul 2, 2023
1 parent b0e2d52 commit 10715b3
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 147 deletions.
44 changes: 17 additions & 27 deletions CriticalMapsKit/Sources/AppFeature/AppFeatureCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public struct AppFeature: ReducerProtocol {
userTrackingMode: UserTrackingFeature.State(userTrackingMode: .follow)
),
socialState: SocialFeature.State = .init(),
settingsState: SettingsFeature.State = .init(),
settingsState: SettingsFeature.State = .init(userSettings: .init()),
nextRideState: NextRideFeature.State = .init(),
requestTimer: RequestTimer.State = .init(),
route: AppRoute? = nil,
Expand All @@ -73,7 +73,7 @@ public struct AppFeature: ReducerProtocol {
userTrackingMode: UserTrackingFeature.State(userTrackingMode: .follow)
)
public var socialState = SocialFeature.State()
public var settingsState = SettingsFeature.State()
public var settingsState = SettingsFeature.State(userSettings: .init())
public var nextRideState = NextRideFeature.State()
public var requestTimer = RequestTimer.State()
public var connectionObserverState = NetworkConnectionObserver.State()
Expand Down Expand Up @@ -200,7 +200,6 @@ public struct AppFeature: ReducerProtocol {
}
}
},
EffectTask(value: .connectionObserver(.observeConnection)),
.task {
await .userSettingsLoaded(
TaskResult {
Expand All @@ -215,7 +214,7 @@ public struct AppFeature: ReducerProtocol {
effects.append(
EffectTask.run { send in
try? await mainQueue.sleep(for: .seconds(3))
await send.send(.presentObservationModeAlert)
await send.callAsFunction(.presentObservationModeAlert)
}
)
}
Expand Down Expand Up @@ -279,7 +278,7 @@ public struct AppFeature: ReducerProtocol {
return .none

case .postLocation:
if state.settingsState.userSettings.isObservationModeEnabled {
if state.settingsState.isObservationModeEnabled {
return .none
}

Expand All @@ -306,7 +305,7 @@ public struct AppFeature: ReducerProtocol {
switch mapFeatureAction {
case .focusRideEvent, .focusNextRide:
if state.bottomSheetPosition != .hidden {
return EffectTask(value: .set(\.$bottomSheetPosition, .relative(0.4)))
return EffectTask.send(.set(\.$bottomSheetPosition, .relative(0.4)))
} else {
return .none
}
Expand All @@ -319,7 +318,7 @@ public struct AppFeature: ReducerProtocol {

if
let coordinate = state.mapFeatureState.location?.coordinate,
state.settingsState.userSettings.rideEventSettings.isEnabled,
state.settingsState.rideEventSettings.isEnabled,
isInitialLocation
{
return .run { send in
Expand All @@ -345,11 +344,11 @@ public struct AppFeature: ReducerProtocol {
case let .setNextRide(ride):
state.mapFeatureState.nextRide = ride
return EffectTask.run { send in
await send.send(.map(.setNextRideBannerVisible(true)))
await send.callAsFunction(.map(.setNextRideBannerVisible(true)))
try? await mainQueue.sleep(for: .seconds(1))
await send.send(.map(.setNextRideBannerExpanded(true)))
await send.callAsFunction(.map(.setNextRideBannerExpanded(true)))
try? await mainQueue.sleep(for: .seconds(8))
await send.send(.map(.setNextRideBannerExpanded(false)))
await send.callAsFunction(.map(.setNextRideBannerExpanded(false)))
}

default:
Expand All @@ -358,9 +357,9 @@ public struct AppFeature: ReducerProtocol {

case let .userSettingsLoaded(result):
let userSettings = (try? result.value) ?? UserSettings()
state.settingsState.userSettings = userSettings
state.settingsState = .init(userSettings: userSettings)
state.nextRideState.rideEventSettings = userSettings.rideEventSettings
let style = state.settingsState.userSettings.appearanceSettings.colorScheme.userInterfaceStyle
let style = state.settingsState.appearanceSettings.colorScheme.userInterfaceStyle
return .merge(
.fireAndForget {
await setUserInterfaceStyle(style)
Expand Down Expand Up @@ -411,18 +410,9 @@ public struct AppFeature: ReducerProtocol {
return .none

case let .setObservationMode(value):
state.settingsState.userSettings.isObservationModeEnabled = value

let userSettings = state.settingsState.userSettings
return .fireAndForget {
await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
try await fileClient.saveUserSettings(userSettings: userSettings)
}
group.addTask {
await userDefaultsClient.setDidShowObservationModePrompt(true)
}
}
state.settingsState.isObservationModeEnabled = value
return .run { _ in
await userDefaultsClient.setDidShowObservationModePrompt(true)
}

case .dismissAlert:
Expand All @@ -442,11 +432,11 @@ public struct AppFeature: ReducerProtocol {
case let .settings(settingsAction):
switch settingsAction {
case .rideevent:
state.nextRideState.rideEventSettings = state.settingsState.userSettings.rideEventSettings
state.nextRideState.rideEventSettings = .init(state.settingsState.rideEventSettings)

guard
let coordinate = state.mapFeatureState.location?.coordinate,
state.settingsState.userSettings.rideEventSettings.isEnabled
state.settingsState.rideEventSettings.isEnabled
else {
return .none
}
Expand Down
6 changes: 1 addition & 5 deletions CriticalMapsKit/Sources/NextRideFeature/NextRideCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,7 @@ public struct NextRideFeature: ReducerProtocol {
.lazy
.filter {
guard let type = $0.rideType else { return true }
return typeSettings
.lazy
.filter(\.isEnabled)
.map(\.type)
.contains(type)
return typeSettings.contains(where: { $0.key == type })
}
.filter(\.enabled)
.sorted { lhs, rhs in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SwiftUI

/// A view to render appearance settings.
public struct AppearanceSettingsView: View {
public typealias State = SharedModels.AppearanceSettings
public typealias State = AppearanceSettings
public typealias Action = AppearanceSettingsFeature.Action

let store: Store<State, Action>
Expand Down
58 changes: 58 additions & 0 deletions CriticalMapsKit/Sources/SettingsFeature/RideEventRadius.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import ComposableArchitecture
import Foundation
import L10n
import SharedModels
import SwiftUI

public struct RideEventRadius: ReducerProtocol {
public init() {}

public struct State: Equatable, Identifiable, Sendable, Codable {
public let id: UUID
public let eventDistance: EventDistance
@BindingState public var isSelected = false

public init(id: UUID = .init(), eventDistance: EventDistance, isSelected: Bool) {
self.id = id
self.eventDistance = eventDistance
self.isSelected = isSelected
}
}

public enum Action: BindableAction, Equatable, Sendable {
case binding(BindingAction<State>)
}

public var body: some ReducerProtocol<State, Action> {
BindingReducer()
}
}

public struct RideEventRadiusView: View {
let store: StoreOf<RideEventRadius>

public var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
SettingsRow {
Button(
action: { viewStore.send(.set(\.$isSelected, true)) },
label: {
HStack(spacing: .grid(3)) {
Text(String(viewStore.eventDistance.displayValue))
.accessibility(label: Text(viewStore.eventDistance.displayValue))
.padding(.vertical, .grid(2))
Spacer()
if viewStore.isSelected {
Image(systemName: "checkmark.circle.fill")
.accessibilityRepresentation {
Text(L10n.A11y.General.selected)
}
}
}
.accessibilityElement(children: .combine)
}
)
}
}
}
}
64 changes: 42 additions & 22 deletions CriticalMapsKit/Sources/SettingsFeature/RideEventSettingsCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,56 @@ import SharedModels

public struct RideEventsSettingsFeature: ReducerProtocol {
public init() {}

public typealias State = SharedModels.RideEventSettings

public struct State: Equatable, Sendable {
@BindingState public var isEnabled: Bool
@BindingState public var eventSearchRadius: EventDistance
public var rideEventTypes: IdentifiedArrayOf<RideEventType.State> = []

public init(
isEnabled: Bool,
eventDistance: EventDistance,
rideEventTypes: [RideEventType.State]
) {
self.isEnabled = isEnabled
self.eventSearchRadius = eventDistance
self.rideEventTypes = .init(uncheckedUniqueElements: rideEventTypes)
}
}

// MARK: Actions

public enum Action: Equatable {
case setRideEventsEnabled(Bool)
case setRideEventTypeEnabled(RideEventSettings.RideEventTypeSetting)
case setRideEventRadius(EventDistance)
public enum Action: BindableAction, Equatable, Sendable {
case binding(BindingAction<State>)
case rideEventType(id: RideEventType.State.ID, action: RideEventType.Action)
}

public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case let .setRideEventsEnabled(value):
state.isEnabled = value
return .none

case let .setRideEventTypeEnabled(type):
guard let index = state.typeSettings.firstIndex(where: { $0.type == type.type }) else {
public var body: some ReducerProtocol<State, Action> {
BindingReducer()

Reduce { _, action in
switch action {
case .binding:
return .none
}
state.typeSettings[index].isEnabled = type.isEnabled
return .none

case let .setRideEventRadius(distance):
guard distance != state.eventDistance else {

case .rideEventType:
return .none
}
state.eventDistance = distance
return .none
}
.forEach(\.rideEventTypes, action: /Action.rideEventType(id:action:)) {
RideEventType()
}
}
}

extension RideEventsSettingsFeature.State {
public init(settings: RideEventSettings) {
self.init(
isEnabled: settings.isEnabled,
eventDistance: settings.eventDistance,
rideEventTypes: settings.typeSettings
.map { RideEventType.State(rideType: $0.key, isEnabled: $0.value) }
.sorted(by: \.rideType.title)
)
}
}
47 changes: 12 additions & 35 deletions CriticalMapsKit/Sources/SettingsFeature/RideEventSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SwiftUIHelpers

/// A view to render next ride event settings
public struct RideEventSettingsView: View {
public typealias State = RideEventSettings
public typealias State = RideEventsSettingsFeature.State
public typealias Action = RideEventsSettingsFeature.Action

let store: Store<State, Action>
Expand All @@ -25,10 +25,7 @@ public struct RideEventSettingsView: View {
SettingsRow {
HStack {
Toggle(
isOn: viewStore.binding(
get: \.isEnabled,
send: Action.setRideEventsEnabled
),
isOn: viewStore.$isEnabled,
label: { Text(L10n.Settings.eventSettingsEnable) }
)
.accessibilityRepresentation(representation: {
Expand All @@ -39,45 +36,29 @@ public struct RideEventSettingsView: View {
}
.accessibilityElement(children: .combine)
}

ZStack(alignment: .top) {
VStack {
SettingsSection(title: L10n.Settings.eventTypes) {
ForEach(viewStore.typeSettings, id: \.type.title) { rideType in
SettingsRow {
Button(
action: { viewStore.send(
.setRideEventTypeEnabled(
.init(
type: rideType.type,
isEnabled: !rideType.isEnabled
)
)
)
},
label: {
RideEventSettingsRow(
title: rideType.type.title,
isEnabled: rideType.isEnabled
)
}
)
}
.accessibilityValue(rideType.isEnabled ? Text(L10n.A11y.General.selected) : Text(""))
ForEachStore(
self.store.scope(state: \.rideEventTypes, action: Action.rideEventType)
) {
RideEventTypeView(store: $0)
}
}

SettingsSection(title: L10n.Settings.eventSearchRadius) {
ForEach(EventDistance.allCases, id: \.self) { radius in
SettingsRow {
Button(
action: { viewStore.send(.setRideEventRadius(radius)) },
action: { viewStore.send(.set(\.$eventSearchRadius, radius)) },
label: {
HStack(spacing: .grid(3)) {
Text(String(radius.displayValue))
.accessibility(label: Text(radius.accessibilityLabel))
.padding(.vertical, .grid(2))
Spacer()
if viewStore.eventDistance == radius {
if viewStore.eventSearchRadius == radius {
Image(systemName: "checkmark.circle.fill")
.accessibilityRepresentation {
Text(L10n.A11y.General.selected)
Expand All @@ -104,24 +85,20 @@ public struct RideEventSettingsView: View {

// MARK: Preview

struct RideEventSettings_Previews: PreviewProvider {
struct RideEventSettings_Previews: PreviewProvider {
static var previews: some View {
Preview {
NavigationView {
RideEventSettingsView(
store: .init(
initialState: .init(
isEnabled: true,
typeSettings: .all,
eventDistance: .near
),
initialState: .init(settings: .init()),
reducer: RideEventsSettingsFeature()._printChanges()
)
)
}
}
}
}
}

// MARK: Helper

Expand Down
Loading

0 comments on commit 10715b3

Please sign in to comment.