Skip to content

Commit 4dd7e6b

Browse files
Screen-Based features (#237)
* wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * gamesessionn feature * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip
1 parent 0362941 commit 4dd7e6b

File tree

216 files changed

+1684
-2032
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

216 files changed

+1684
-2032
lines changed

.gitignore

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,6 @@ Package.resolved
4848
*swiftpm/
4949
.build/
5050

51-
# CocoaPods
52-
#
53-
# We recommend against adding the Pods directory to your .gitignore. However
54-
# you should judge for yourself, the pros and cons are mentioned at:
55-
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56-
#
57-
# Pods/
58-
#
59-
# Add this line if you want to avoid checking in source code from the Xcode workspace
60-
# *.xcworkspace
61-
62-
# Carthage
63-
#
64-
# Add this line if you want to avoid checking in source code from Carthage dependencies.
65-
# Carthage/Checkouts
66-
67-
Carthage/Build/
68-
69-
# Accio dependency management
70-
Dependencies/
71-
.accio/
72-
7351
# fastlane
7452
#
7553
# It is recommended to not store the screenshots in the git repo.

App/WildWestOnline.xcodeproj/project.pbxproj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
2699EF0129D93AD00030ACCD /* WildWestOnlineApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2699EF0029D93AD00030ACCD /* WildWestOnlineApp.swift */; };
1111
2699EF0529D93AD10030ACCD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2699EF0429D93AD10030ACCD /* Assets.xcassets */; };
12-
26F4048D2EB5CE5700F5FCA9 /* AppBootstrap in Frameworks */ = {isa = PBXBuildFile; productRef = 26F4048C2EB5CE5700F5FCA9 /* AppBootstrap */; };
12+
98A1A0BA2EE4A1D7003EB768 /* AppBuilder in Frameworks */ = {isa = PBXBuildFile; productRef = 98A1A0B92EE4A1D7003EB768 /* AppBuilder */; };
1313
98E868E62D9D19CC00EF4582 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98E868E52D9D19CC00EF4582 /* LaunchScreen.storyboard */; };
1414
/* End PBXBuildFile section */
1515

@@ -26,7 +26,7 @@
2626
isa = PBXFrameworksBuildPhase;
2727
buildActionMask = 2147483647;
2828
files = (
29-
26F4048D2EB5CE5700F5FCA9 /* AppBootstrap in Frameworks */,
29+
98A1A0BA2EE4A1D7003EB768 /* AppBuilder in Frameworks */,
3030
);
3131
runOnlyForDeploymentPostprocessing = 0;
3232
};
@@ -77,7 +77,7 @@
7777
);
7878
name = WildWestOnline;
7979
packageProductDependencies = (
80-
26F4048C2EB5CE5700F5FCA9 /* AppBootstrap */,
80+
98A1A0B92EE4A1D7003EB768 /* AppBuilder */,
8181
);
8282
productName = ReduxGameDSL;
8383
productReference = 2699EEFD29D93AD00030ACCD /* WildWestOnline.app */;
@@ -419,9 +419,10 @@
419419
/* End XCLocalSwiftPackageReference section */
420420

421421
/* Begin XCSwiftPackageProductDependency section */
422-
26F4048C2EB5CE5700F5FCA9 /* AppBootstrap */ = {
422+
98A1A0B92EE4A1D7003EB768 /* AppBuilder */ = {
423423
isa = XCSwiftPackageProductDependency;
424-
productName = AppBootstrap;
424+
package = 26F4048B2EB5CE5700F5FCA9 /* XCLocalSwiftPackageReference "../Modules" */;
425+
productName = AppBuilder;
425426
};
426427
/* End XCSwiftPackageProductDependency section */
427428
};

App/WildWestOnlineApp.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Created by Hugues Telolahy on 02/04/2023.
66
//
77
import SwiftUI
8-
import AppBootstrap
8+
import AppBuilder
99

1010
@main
1111
struct WildWestOnlineApp: App {

Modules/.swiftlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ opt_in_rules:
102102
- prefixed_toplevel_constant
103103
- private_action
104104
- private_outlet
105-
- private_subject
105+
# - private_subject
106106
# - private_swiftui_state
107107
# - prohibited_interface_builder
108108
- prohibited_super_call

Modules/AppBootstrap/Sources/AppBuilder.swift

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// AppBuilder.swift
3+
// WildWestOnline
4+
//
5+
// Created by Hugues Telolahy on 31/10/2025.
6+
//
7+
import SwiftUI
8+
import Redux
9+
import AppFeature
10+
import PreferencesClient
11+
import PreferencesClientLive
12+
import AudioClient
13+
import AudioClientLive
14+
import CardLibrary
15+
import CardLibraryLive
16+
import GameCore
17+
18+
@MainActor
19+
public enum AppBuilder {
20+
public static func build() -> some View {
21+
var dependencies = Dependencies()
22+
dependencies.preferencesClient = PreferencesClient.live()
23+
dependencies.audioClient = AudioClient.live()
24+
dependencies.queueModifierClient = QueueModifierClient.live(handlers: QueueModifiers.allHandlers)
25+
dependencies.cardLibrary = CardLibrary.live()
26+
27+
let store = Store<AppFeature.State, AppFeature.Action>(
28+
initialState: .init(),
29+
reducer: AppFeature.reducer,
30+
dependencies: dependencies
31+
)
32+
33+
return AppView {
34+
store
35+
}
36+
}
37+
}

Modules/AppFeature/Sources/AppFeature.swift

Lines changed: 86 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,111 +6,134 @@
66
//
77

88
import Redux
9-
import NavigationFeature
9+
import HomeFeature
1010
import SettingsFeature
11-
import GameFeature
12-
import AudioClient
13-
import PreferencesClient
14-
15-
public typealias AppStore = Store<AppFeature.State, AppFeature.Action>
11+
import GameSessionFeature
1612

1713
public enum AppFeature {
18-
/// Global app state
19-
/// Organize State Structure Based on Data Types, Not Components
20-
/// https://redux.js.org/style-guide/#organize-state-structure-based-on-data-types-not-components
21-
public struct State: Codable, Equatable, Sendable {
22-
public let cardLibrary: CardLibrary
23-
public var navigation: AppNavigationFeature.State
24-
public var settings: SettingsFeature.State
25-
public var game: GameFeature.State?
14+
public struct State: Equatable {
15+
var path: [Destination]
16+
var home: HomeFeature.State
17+
var gameSession: GameSessionFeature.State
18+
var settings: SettingsFeature.State?
19+
20+
public enum Destination: Hashable {
21+
case gameSession
22+
}
2623

2724
public init(
28-
cardLibrary: CardLibrary,
29-
navigation: AppNavigationFeature.State,
30-
settings: SettingsFeature.State,
31-
game: GameFeature.State? = nil
25+
path: [Destination] = [],
26+
home: HomeFeature.State = .init(),
27+
gameSession: GameSessionFeature.State = .init(),
28+
settings: SettingsFeature.State? = nil
3229
) {
33-
self.cardLibrary = cardLibrary
34-
self.navigation = navigation
30+
self.path = path
31+
self.home = home
3532
self.settings = settings
36-
self.game = game
37-
}
38-
39-
public struct CardLibrary: Codable, Equatable, Sendable {
40-
public let cards: [Card]
41-
public let deck: [String]
42-
public let specialSounds: [Card.ActionName: [String: AudioClient.Sound]]
43-
44-
public init(
45-
cards: [Card] = [],
46-
deck: [String] = [],
47-
specialSounds: [Card.ActionName: [String: AudioClient.Sound]] = [:]
48-
) {
49-
self.cards = cards
50-
self.deck = deck
51-
self.specialSounds = specialSounds
52-
}
33+
self.gameSession = gameSession
5334
}
5435
}
5536

5637
public enum Action {
57-
case start
58-
case quit
59-
case setGame(GameFeature.State)
60-
case unsetGame
61-
case navigation(AppNavigationFeature.Action)
38+
// View
39+
case setPath([State.Destination])
40+
case setSettingsPresented(Bool)
41+
42+
// Internal
43+
case home(HomeFeature.Action)
6244
case settings(SettingsFeature.Action)
63-
case game(GameFeature.Action)
45+
case gameSession(GameSessionFeature.Action)
6446
}
6547

6648
public static var reducer: Reducer<State, Action> {
6749
combine(
6850
reducerMain,
6951
pullback(
70-
GameFeature.reducer,
71-
state: { globalState in
72-
globalState.game != nil ? \.game! : nil
52+
HomeFeature.reducer,
53+
state: { _ in
54+
\.home
7355
},
7456
action: { globalAction in
75-
if case let .game(localAction) = globalAction {
57+
if case let .home(localAction) = globalAction {
7658
return localAction
7759
}
7860
return nil
7961
},
80-
embedAction: Action.game
62+
embedAction: {
63+
.home($0)
64+
}
8165
),
8266
pullback(
83-
SettingsFeature.reducer,
67+
GameSessionFeature.reducer,
8468
state: { _ in
85-
\.settings
69+
\.gameSession
8670
},
8771
action: { globalAction in
88-
if case let .settings(localAction) = globalAction {
72+
if case let .gameSession(localAction) = globalAction {
8973
return localAction
9074
}
9175
return nil
9276
},
93-
embedAction: Action.settings
77+
embedAction: {
78+
.gameSession($0)
79+
}
9480
),
9581
pullback(
96-
AppNavigationFeature.reducer,
97-
state: { _ in
98-
\.navigation
82+
SettingsFeature.reducer,
83+
state: {
84+
$0.settings != nil ? \.settings! : nil
9985
},
10086
action: { globalAction in
101-
if case let .navigation(localAction) = globalAction {
87+
if case let .settings(localAction) = globalAction {
10288
return localAction
10389
}
10490
return nil
10591
},
106-
embedAction: Action.navigation
107-
),
108-
pullback(
109-
reducerSound,
110-
state: { _ in \.self },
111-
action: { $0 },
112-
embedAction: \.self
92+
embedAction: {
93+
.settings($0)
94+
}
11395
)
11496
)
11597
}
98+
99+
private static func reducerMain(
100+
into state: inout State,
101+
action: Action,
102+
dependencies: Dependencies
103+
) -> Effect<Action> {
104+
switch action {
105+
case .setPath(let path):
106+
state.path = path
107+
return .none
108+
109+
case .setSettingsPresented(let presented):
110+
if presented {
111+
state.settings = .init()
112+
} else {
113+
state.settings = nil
114+
}
115+
return .none
116+
117+
case .home(.delegate(.settings)):
118+
return .run { .setSettingsPresented(true) }
119+
120+
case .home(.delegate(.play)):
121+
return .run { .setPath([.gameSession]) }
122+
123+
case .home:
124+
return .none
125+
126+
case .settings:
127+
return .none
128+
129+
case .gameSession(.delegate(.settings)):
130+
return .run { .setSettingsPresented(true) }
131+
132+
case .gameSession(.delegate(.quit)):
133+
return .run { .setPath([]) }
134+
135+
case .gameSession:
136+
return .none
137+
}
138+
}
116139
}

0 commit comments

Comments
 (0)