1- //
2- // NumberEntryView.swift
3- // Construct
4- //
5- // Created by Thomas Visser on 02/01/2020.
6- // Copyright © 2020 Thomas Visser. All rights reserved.
7- //
8-
91import Foundation
102import SwiftUI
113import ComposableArchitecture
@@ -14,103 +6,154 @@ import Dice
146import GameModels
157import Helpers
168
17- // A view that allows entry of a number, either directly or through a simulated dice roll
18- struct NumberEntryView : View {
19- var store : Store < NumberEntryViewState , NumberEntryViewAction >
20- @ObservedObject var viewStore : ViewStore < NumberEntryViewState , NumberEntryViewAction >
9+ struct NumberEntryFeature : Reducer {
10+ struct State : Equatable {
11+ var mode : Mode
12+ var pad : NumberPadFeature . State
13+ var dice : DiceCalculator . State
2114
22- init ( store: Store < NumberEntryViewState , NumberEntryViewAction > ) {
23- self . store = store
24- self . viewStore = ViewStore ( store, observe: \. self)
25- }
15+ init (
16+ mode: Mode = . dice,
17+ pad: NumberPadFeature . State = NumberPadFeature . State ( value: 0 ) ,
18+ dice: DiceCalculator . State = . editingExpression( )
19+ ) {
20+ self . mode = mode
21+ self . pad = pad
22+ self . dice = dice
23+ }
2624
27- var body : some View {
28- VStack {
29- Picker ( " Type " , selection: viewStore. binding ( get: { $0. mode } , send: { . mode( $0) } ) . animation ( . spring( ) ) ) {
30- Text ( " Roll " ) . tag ( NumberEntryViewState . Mode. dice)
31- Text ( " Manual " ) . tag ( NumberEntryViewState . Mode. pad)
32- } . pickerStyle ( SegmentedPickerStyle ( ) )
33-
34- if viewStore. state. mode == . dice {
35- DiceCalculatorView ( store: store. scope ( state: { $0. diceState } , action: { . dice( $0) } ) )
36- } else if viewStore. state. mode == . pad {
37- NumberPadView ( store: store. scope ( state: { $0. padState } , action: { . pad( $0) } ) )
25+ var value : Int ? {
26+ switch mode {
27+ case . pad:
28+ return pad. value
29+ case . dice:
30+ return dice. result ( includingIntermediary: false ) ? . total
3831 }
3932 }
33+
34+ enum Mode : Hashable {
35+ case pad
36+ case dice
37+ }
4038 }
41- }
4239
43- struct NumberEntryViewState : Hashable {
44- var mode : Mode
45- var padState : NumberPadViewState
46- var diceState : DiceCalculator . State
40+ enum Action : Equatable {
41+ case mode( State . Mode )
42+ case pad( NumberPadFeature . Action )
43+ case dice( DiceCalculator . Action )
44+ }
4745
48- var value : Int ? {
49- switch mode {
50- case . pad: return padState. value
51- case . dice: return diceState. result ( includingIntermediary: false ) ? . total
52- }
46+ typealias Environment = NumberEntryViewEnvironment
47+
48+ let environment : Environment
49+
50+ init ( environment: Environment ) {
51+ self . environment = environment
5352 }
5453
55- enum Mode : Hashable {
56- case pad
57- case dice
54+ var body : some ReducerOf < Self > {
55+ Scope ( state: \State . pad, action: / Action. pad) {
56+ NumberPadFeature ( )
57+ }
58+
59+ Scope ( state: \State . dice, action: / Action. dice) {
60+ DiceCalculator ( environment: environment)
61+ }
62+
63+ Reduce { state, action in
64+ switch action {
65+ case . mode( let mode) :
66+ state. mode = mode
67+ case . pad, . dice:
68+ break
69+ }
70+ return . none
71+ }
5872 }
5973}
6074
61- enum NumberEntryViewAction : Equatable {
62- case mode( NumberEntryViewState . Mode )
63- case pad( NumberPadViewAction )
64- case dice( DiceCalculator . Action )
65- }
75+ typealias NumberEntryViewEnvironment = EnvironmentWithModifierFormatter & EnvironmentWithMainQueue & EnvironmentWithDiceLog
6676
67- extension NumberEntryViewState {
68- static func pad( value: Int , expression: DiceExpression ? = nil ) -> NumberEntryViewState {
69- return NumberEntryViewState ( mode: . pad, padState: NumberPadViewState ( value: value) , diceState: expression. map { . rollingExpression( $0) } ?? . editingExpression( ) )
77+ extension NumberEntryFeature . State {
78+ static func pad( value: Int , expression: DiceExpression ? = nil ) -> Self {
79+ Self (
80+ mode: . pad,
81+ pad: NumberPadFeature . State ( value: value) ,
82+ dice: expression. map { . rollingExpression( $0) } ?? . editingExpression( )
83+ )
7084 }
7185
72- static func dice( _ state: DiceCalculator . State ) -> NumberEntryViewState {
73- return NumberEntryViewState ( mode: . dice, padState: NumberPadViewState ( value: 0 ) , diceState: state)
86+ static func dice( _ state: DiceCalculator . State ) -> Self {
87+ Self (
88+ mode: . dice,
89+ pad: NumberPadFeature . State ( value: 0 ) ,
90+ dice: state
91+ )
7492 }
7593
76- static func initiative( combatant: Combatant ) -> NumberEntryViewState {
94+ static func initiative( combatant: Combatant ) -> Self {
7795 if combatant. definition. player != nil {
78- return NumberEntryViewState . pad (
96+ return . pad(
7997 value: combatant. initiative ?? 0 ,
8098 expression: combatant. definition. initiativeModifier. map { 1 . d ( 20 ) + $0 }
8199 )
82100 } else if let mod = combatant. definition. initiativeModifier {
83- return NumberEntryViewState . dice ( . rollingExpression( 1 . d ( 20 ) + mod, prefilledResult: combatant. initiative) )
101+ return . dice( . rollingExpression( 1 . d ( 20 ) + mod, prefilledResult: combatant. initiative) )
84102 } else if let initiative = combatant. initiative {
85- return NumberEntryViewState . dice ( . rollingExpression( 1 . d ( 20 ) , prefilledResult: initiative) )
103+ return . dice( . rollingExpression( 1 . d ( 20 ) , prefilledResult: initiative) )
86104 } else {
87- return NumberEntryViewState . dice ( . editingExpression( 1 . d ( 20 ) ) )
105+ return . dice( . editingExpression( 1 . d ( 20 ) ) )
88106 }
89107 }
90108
91- static var reducer : AnyReducer < Self , NumberEntryViewAction , NumberEntryViewEnvironment > = AnyReducer . combine (
92- AnyReducer { state, action, _ in
93- switch action {
94- case . mode( let m) :
95- state. mode = m
96- case . pad, . dice: break // handled by reducers below
109+ static let nullInstance = Self (
110+ mode: . dice,
111+ pad: NumberPadFeature . State ( value: 0 ) ,
112+ dice: DiceCalculator . State ( displayOutcomeExternally: false , rollOnAppear: false , expression: . number( 0 ) , mode: . editingExpression)
113+ )
114+ }
115+
116+ struct NumberEntryView : View {
117+ let store : StoreOf < NumberEntryFeature >
118+
119+ init ( store: StoreOf < NumberEntryFeature > ) {
120+ self . store = store
121+ }
122+
123+ var body : some View {
124+ WithViewStore ( store, observe: \. self) { viewStore in
125+ VStack {
126+ Picker (
127+ " Type " ,
128+ selection: viewStore. binding (
129+ get: \. mode,
130+ send: NumberEntryFeature . Action. mode
131+ ) . animation ( . spring( ) )
132+ ) {
133+ Text ( " Roll " ) . tag ( NumberEntryFeature . State. Mode. dice)
134+ Text ( " Manual " ) . tag ( NumberEntryFeature . State. Mode. pad)
135+ }
136+ . pickerStyle ( . segmented)
137+
138+ if viewStore. state. mode == . dice {
139+ DiceCalculatorView ( store: store. scope ( state: \. dice, action: NumberEntryFeature . Action. dice) )
140+ } else {
141+ NumberPadView ( store: store. scope ( state: \. pad, action: NumberEntryFeature . Action. pad) )
142+ }
97143 }
98- return . none
99- } ,
100- NumberPadViewState . reducer. pullback ( state: \. padState, action: / NumberEntryViewAction. pad, environment: { _ in ( ) } ) ,
101- AnyReducer { env in
102- DiceCalculator ( environment: env)
103144 }
104- . pullback (
105- state: \. diceState,
106- action: / NumberEntryViewAction. dice,
107- environment: { $0 }
108- )
109- )
145+ }
110146}
111147
112- typealias NumberEntryViewEnvironment = EnvironmentWithModifierFormatter & EnvironmentWithMainQueue & EnvironmentWithDiceLog
113148
114- extension NumberEntryViewState {
115- static let nullInstance = NumberEntryViewState ( mode: . dice, padState: NumberPadViewState ( value: 0 ) , diceState: DiceCalculator . State ( displayOutcomeExternally: false , rollOnAppear: false , expression: . number( 0 ) , mode: . editingExpression) )
149+ typealias NumberEntryViewState = NumberEntryFeature . State
150+ typealias NumberEntryViewAction = NumberEntryFeature . Action
151+
152+ extension NumberEntryFeature . State {
153+ static var reducer : AnyReducer < Self , NumberEntryFeature . Action , NumberEntryViewEnvironment > {
154+ AnyReducer { state, action, environment in
155+ let feature = NumberEntryFeature ( environment: environment)
156+ return feature. reduce ( into: & state, action: action)
157+ }
158+ }
116159}
0 commit comments