From c9bf108b51eed0a0f6efefdcebe97a1a94a107b5 Mon Sep 17 00:00:00 2001 From: CypherPoet Date: Thu, 23 Jan 2020 15:49:26 -0600 Subject: [PATCH] Complete Challenge 2 > Add a settings screen that has a single option: Whether or not a card goes back into deck if it was answered incorrectly. --- .../Flashzilla.xcodeproj/project.pbxproj | 45 +++++++++++ .../xcshareddata/swiftpm/Package.resolved | 9 +++ .../Flashzilla/App/SceneDelegate.swift | 3 + .../Flashzilla/Data/State/AppState.swift | 37 ++++++++++ .../Flashzilla/Data/State/SettingsState.swift | 35 +++++++++ .../Preview Data/PreviewData+AppStore.swift | 20 +++++ .../Reusables/Views/Card/CardView.swift | 3 +- .../Views/Card/DraggableCardView.swift | 5 ++ .../CardDeckContainerView+ViewModel.swift | 28 +++++-- .../Card Deck/CardDeckContainerView.swift | 13 +++- .../Flashzilla/Scenes/RootView.swift | 1 + .../Settings/SettingsContainerView.swift | 46 ++++++++++++ .../Scenes/Settings/SettingsFormView.swift | 74 +++++++++++++++++++ 13 files changed, 308 insertions(+), 11 deletions(-) create mode 100644 day-086/Projects/Flashzilla/Flashzilla/Data/State/AppState.swift create mode 100644 day-086/Projects/Flashzilla/Flashzilla/Data/State/SettingsState.swift create mode 100644 day-086/Projects/Flashzilla/Flashzilla/Preview Content/Preview Data/PreviewData+AppStore.swift create mode 100644 day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsContainerView.swift create mode 100644 day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsFormView.swift diff --git a/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.pbxproj b/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.pbxproj index 6723047..6d8e313 100644 --- a/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.pbxproj +++ b/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.pbxproj @@ -44,6 +44,12 @@ F378AA2523D7B91E00296A76 /* CardDeck+Computeds.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA2423D7B91E00296A76 /* CardDeck+Computeds.swift */; }; F378AA3023D8909C00296A76 /* KeyboardAvoider in Frameworks */ = {isa = PBXBuildFile; productRef = F378AA2F23D8909C00296A76 /* KeyboardAvoider */; }; F378AA3223D8A6EA00296A76 /* Card+FetchHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA3123D8A6EA00296A76 /* Card+FetchHelpers.swift */; }; + F378AA3623D91B5100296A76 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA3523D91B5100296A76 /* AppState.swift */; }; + F378AA3823D91B8C00296A76 /* SettingsState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA3723D91B8C00296A76 /* SettingsState.swift */; }; + F378AA3B23D91BD900296A76 /* Burritos in Frameworks */ = {isa = PBXBuildFile; productRef = F378AA3A23D91BD900296A76 /* Burritos */; }; + F378AA3E23D91DFE00296A76 /* SettingsFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA3D23D91DFE00296A76 /* SettingsFormView.swift */; }; + F378AA4023D91E1500296A76 /* SettingsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA3F23D91E1500296A76 /* SettingsContainerView.swift */; }; + F378AA4223D9215400296A76 /* PreviewData+AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F378AA4123D9215400296A76 /* PreviewData+AppStore.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -82,6 +88,11 @@ F378AA2123D7B8D900296A76 /* CardDeck+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CardDeck+CoreDataProperties.swift"; sourceTree = ""; }; F378AA2423D7B91E00296A76 /* CardDeck+Computeds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CardDeck+Computeds.swift"; sourceTree = ""; }; F378AA3123D8A6EA00296A76 /* Card+FetchHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Card+FetchHelpers.swift"; sourceTree = ""; }; + F378AA3523D91B5100296A76 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; + F378AA3723D91B8C00296A76 /* SettingsState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsState.swift; sourceTree = ""; }; + F378AA3D23D91DFE00296A76 /* SettingsFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFormView.swift; sourceTree = ""; }; + F378AA3F23D91E1500296A76 /* SettingsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsContainerView.swift; sourceTree = ""; }; + F378AA4123D9215400296A76 /* PreviewData+AppStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreviewData+AppStore.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,6 +101,7 @@ buildActionMask = 2147483647; files = ( F378A9EB23D43C3200296A76 /* CypherPoetSwiftUIKit in Frameworks */, + F378AA3B23D91BD900296A76 /* Burritos in Frameworks */, F378A9EE23D43C5600296A76 /* CypherPoetCoreDataKit in Frameworks */, F378AA3023D8909C00296A76 /* KeyboardAvoider in Frameworks */, F378A9F123D43C6F00296A76 /* CypherPoetSwiftUIAnimationKit in Frameworks */, @@ -152,6 +164,7 @@ F378A9D623D438D900296A76 /* Scenes */ = { isa = PBXGroup; children = ( + F378AA3C23D91D9700296A76 /* Settings */, F378A9D923D4393100296A76 /* RootView.swift */, F378AA0023D48CA900296A76 /* Card Deck */, ); @@ -191,6 +204,8 @@ F378A9DC23D4395D00296A76 /* State */ = { isa = PBXGroup; children = ( + F378AA3523D91B5100296A76 /* AppState.swift */, + F378AA3723D91B8C00296A76 /* SettingsState.swift */, ); path = State; sourceTree = ""; @@ -210,6 +225,7 @@ children = ( F378A9E323D43A1700296A76 /* PreviewData.swift */, F378A9F223D43CA900296A76 /* PreviewData+Cards.swift */, + F378AA4123D9215400296A76 /* PreviewData+AppStore.swift */, ); path = "Preview Data"; sourceTree = ""; @@ -300,6 +316,15 @@ path = "Card Deck"; sourceTree = ""; }; + F378AA3C23D91D9700296A76 /* Settings */ = { + isa = PBXGroup; + children = ( + F378AA3D23D91DFE00296A76 /* SettingsFormView.swift */, + F378AA3F23D91E1500296A76 /* SettingsContainerView.swift */, + ); + path = Settings; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -321,6 +346,7 @@ F378A9ED23D43C5600296A76 /* CypherPoetCoreDataKit */, F378A9F023D43C6F00296A76 /* CypherPoetSwiftUIAnimationKit */, F378AA2F23D8909C00296A76 /* KeyboardAvoider */, + F378AA3A23D91BD900296A76 /* Burritos */, ); productName = Flashzilla; productReference = F378A9BB23D438AE00296A76 /* Flashzilla.app */; @@ -355,6 +381,7 @@ F378A9EC23D43C5600296A76 /* XCRemoteSwiftPackageReference "CypherPoetCoreDataKit" */, F378A9EF23D43C6E00296A76 /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIAnimationKit" */, F378AA2E23D8909C00296A76 /* XCRemoteSwiftPackageReference "KeyboardAvoider" */, + F378AA3923D91BD900296A76 /* XCRemoteSwiftPackageReference "Burritos" */, ); productRefGroup = F378A9BC23D438AE00296A76 /* Products */; projectDirPath = ""; @@ -391,6 +418,7 @@ F378AA0C23D4A5D300296A76 /* View+Stacked.swift in Sources */, F378A9F723D44E3900296A76 /* Card+CoreDataProperties.swift in Sources */, F378A9E823D43B6A00296A76 /* CurrentApplication.swift in Sources */, + F378AA3823D91B8C00296A76 /* SettingsState.swift in Sources */, F378AA2323D7B8D900296A76 /* CardDeck+CoreDataProperties.swift in Sources */, F378A9FD23D47E1000296A76 /* CardView+ViewModel.swift in Sources */, F378A9E423D43A1700296A76 /* PreviewData.swift in Sources */, @@ -400,11 +428,14 @@ F378AA1323D63AA700296A76 /* NumberFormatters.swift in Sources */, F378A9C423D438AE00296A76 /* Flashzilla.xcdatamodeld in Sources */, F378AA0E23D4CC6400296A76 /* DraggableCardView.swift in Sources */, + F378AA3E23D91DFE00296A76 /* SettingsFormView.swift in Sources */, F378A9BF23D438AE00296A76 /* AppDelegate.swift in Sources */, F378A9E623D43A6900296A76 /* CoreDataManager+Utils.swift in Sources */, F378AA1023D5BAD500296A76 /* CardDeckView.swift in Sources */, F378AA1723D733EF00296A76 /* EditDeckView.swift in Sources */, F378AA1523D7156200296A76 /* Card+AnswerState.swift in Sources */, + F378AA3623D91B5100296A76 /* AppState.swift in Sources */, + F378AA4023D91E1500296A76 /* SettingsContainerView.swift in Sources */, F378AA1923D7359B00296A76 /* EditDeckView+ViewModel.swift in Sources */, F378A9F323D43CAA00296A76 /* PreviewData+Cards.swift in Sources */, F378AA0223D48CB900296A76 /* CardDeckContainerView.swift in Sources */, @@ -412,6 +443,7 @@ F378A9FB23D47DD900296A76 /* CardView.swift in Sources */, F378AA0723D4985200296A76 /* FetchedResultsControlling.swift in Sources */, F378A9C123D438AE00296A76 /* SceneDelegate.swift in Sources */, + F378AA4223D9215400296A76 /* PreviewData+AppStore.swift in Sources */, F378AA2523D7B91E00296A76 /* CardDeck+Computeds.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -640,6 +672,14 @@ minimumVersion = 1.0.1; }; }; + F378AA3923D91BD900296A76 /* XCRemoteSwiftPackageReference "Burritos" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/guillermomuntaner/Burritos.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.3; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -663,6 +703,11 @@ package = F378AA2E23D8909C00296A76 /* XCRemoteSwiftPackageReference "KeyboardAvoider" */; productName = KeyboardAvoider; }; + F378AA3A23D91BD900296A76 /* Burritos */ = { + isa = XCSwiftPackageProductDependency; + package = F378AA3923D91BD900296A76 /* XCRemoteSwiftPackageReference "Burritos" */; + productName = Burritos; + }; /* End XCSwiftPackageProductDependency section */ /* Begin XCVersionGroup section */ diff --git a/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4f06c1b..e1ba4cb 100644 --- a/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/day-086/Projects/Flashzilla/Flashzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "Burritos", + "repositoryURL": "https://github.com/guillermomuntaner/Burritos.git", + "state": { + "branch": null, + "revision": "309dbe1b5b3af8839ca6a7ebb2ad6ddf041bf420", + "version": "0.0.3" + } + }, { "package": "CypherPoetCoreDataKit", "repositoryURL": "https://github.com/CypherPoet/CypherPoetCoreDataKit.git", diff --git a/day-086/Projects/Flashzilla/Flashzilla/App/SceneDelegate.swift b/day-086/Projects/Flashzilla/Flashzilla/App/SceneDelegate.swift index 6d066b2..ddcdad0 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/App/SceneDelegate.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/App/SceneDelegate.swift @@ -30,10 +30,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { PreviewData.setupSimulatorPreviewData(in: context) #endif + let store = AppStore(initialState: .init(), appReducer: appReducer) + // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath. // Add `@Environment(\.managedObjectContext)` in the views that will need the context. let entryView = RootView() .environment(\.managedObjectContext, context) + .environmentObject(store) window.rootViewController = UIHostingController(rootView: entryView) diff --git a/day-086/Projects/Flashzilla/Flashzilla/Data/State/AppState.swift b/day-086/Projects/Flashzilla/Flashzilla/Data/State/AppState.swift new file mode 100644 index 0000000..c9e83b5 --- /dev/null +++ b/day-086/Projects/Flashzilla/Flashzilla/Data/State/AppState.swift @@ -0,0 +1,37 @@ + // +// AppState.swift +// Flashzilla +// +// Created by CypherPoet on 1/22/20. +// ✌️ +// + + +import Foundation +import CypherPoetSwiftUIKit_DataFlowUtils + + +struct AppState { + var settingsState = SettingsState() +} + + + +//enum AppSideEffect: SideEffect {} + + +enum AppAction { + case settings(_ action: SettingsAction) +} + + +// MARK: - Reducer +let appReducer = Reducer { appState, action in + switch action { + case .settings(let action): + settingsReducer.reduce(&appState.settingsState, action) + } +} + + +typealias AppStore = Store diff --git a/day-086/Projects/Flashzilla/Flashzilla/Data/State/SettingsState.swift b/day-086/Projects/Flashzilla/Flashzilla/Data/State/SettingsState.swift new file mode 100644 index 0000000..fc9990e --- /dev/null +++ b/day-086/Projects/Flashzilla/Flashzilla/Data/State/SettingsState.swift @@ -0,0 +1,35 @@ +// +// SettingsState.swift +// Flashzilla +// +// Created by CypherPoet on 1/22/20. +// ✌️ +// + + +import CypherPoetSwiftUIKit_DataFlowUtils +import UserDefault +import Combine + + +struct SettingsState { + @UserDefault("settings-state-restacks-incorrect-cards", defaultValue: false) + var restacksIncorrectCards: Bool +} + + +enum SettingsAction { + case restacksIncorrectCardsSet(Bool) +} + + + +// MARK: - Reducer +let settingsReducer: Reducer = Reducer( + reduce: { state, action in + switch action { + case .restacksIncorrectCardsSet(let value): + state.restacksIncorrectCards = value + } + } +) diff --git a/day-086/Projects/Flashzilla/Flashzilla/Preview Content/Preview Data/PreviewData+AppStore.swift b/day-086/Projects/Flashzilla/Flashzilla/Preview Content/Preview Data/PreviewData+AppStore.swift new file mode 100644 index 0000000..d755cf9 --- /dev/null +++ b/day-086/Projects/Flashzilla/Flashzilla/Preview Content/Preview Data/PreviewData+AppStore.swift @@ -0,0 +1,20 @@ +// +// PreviewData+AppStore.swift +// Flashzilla +// +// Created by CypherPoet on 1/22/20. +// ✌️ +// + +import Foundation + + +extension PreviewData { + + enum AppStores { + + static let `default`: AppStore = { + AppStore(initialState: .init(), appReducer: appReducer) + }() + } +} diff --git a/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/CardView.swift b/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/CardView.swift index 1764e01..659a039 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/CardView.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/CardView.swift @@ -63,7 +63,8 @@ extension CardView { private var cardContent: some View { Group { - if isAccessibilityEnabled { +// if isAccessibilityEnabled { // 📝 This appears to become true whenever a sheet is presented and dismissed over the deck view (Xcode 11.3.1) + if false { Text(isShowingAnswer ? viewModel.cardAnswerText : viewModel.cardPromptText) .font(.largeTitle) } else { diff --git a/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/DraggableCardView.swift b/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/DraggableCardView.swift index 08ba934..e0e2b48 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/DraggableCardView.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/Reusables/Views/Card/DraggableCardView.swift @@ -42,11 +42,16 @@ extension DraggableCardView: View { .clipShape( RoundedRectangle(cornerRadius: min(geometry.size.width, geometry.size.height) * 0.08) ) + .animation(.easeIn(duration: 0.25)) ) .animation(nil) .rotationEffect(self.cardRotation) .offset(self.cardOffset) + + // TODO: We'd need to manually animate this back into position if we don't + // want to have to cancel the animations before it. (Look into using @State instead + // of @GestureState -- since the latter automatically handles resettting on end) .animation(.spring()) .gesture(self.dragGesture) } diff --git a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView+ViewModel.swift b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView+ViewModel.swift index 9718faa..7e702ba 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView+ViewModel.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView+ViewModel.swift @@ -29,7 +29,7 @@ extension CardDeckContainerView { var isTimerActive = false var roundDuration: TimeInterval - + var settingsState: SettingsState // MARK: - Published Outputs @Published var cards: [Card] = [] @@ -40,11 +40,13 @@ extension CardDeckContainerView { // MARK: - Init init( cardDeck: CardDeck, - roundDuration: TimeInterval = 100.0 + roundDuration: TimeInterval = 100.0, + settingsState: SettingsState = .init() ) { self.cardDeck = cardDeck self.roundDuration = roundDuration self.timeRemaining = roundDuration + self.settingsState = settingsState super.init() @@ -79,10 +81,21 @@ extension CardDeckContainerView.ViewModel { private var visibleCardsPublisher: AnyPublisher<[Card], Never> { - $cards - .map { $0.filter { $0.answerState == .unanswered } } - .print("visibleCardsPublisher") - .eraseToAnyPublisher() + Publishers.CombineLatest( + $cards, + CurrentValueSubject(settingsState) + ) + .map { (cards, settingsState) in + cards.filter { card in + let answerState = card.answerState + + // An optimization here would be to include sorting logic that accounted + // for the answer state. That way, incorrect cards could slide to the back. + return answerState == .unanswered || + (settingsState.restacksIncorrectCards && answerState == .incorrect) + } + } + .eraseToAnyPublisher() } } @@ -109,7 +122,7 @@ extension CardDeckContainerView.ViewModel { var unansweredCountText: String { let count = cardDeck.unansweredCount - return "\(count) \(count == 1 ? "Card" : "Cards") Unattempted" + return "\(count) \(count == 1 ? "Card" : "Cards") Unanswered" } } @@ -146,7 +159,6 @@ extension CardDeckContainerView.ViewModel { func record(_ answerState: Card.AnswerState, forCardAt index: Int) { let card = visibleCards[index] - guard let managedObjectContext = card.managedObjectContext else { fatalError() } card.answerState = answerState diff --git a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView.swift b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView.swift index dd91d28..7eca017 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Card Deck/CardDeckContainerView.swift @@ -11,6 +11,7 @@ import CypherPoetSwiftUIKit struct CardDeckContainerView { + @EnvironmentObject var store: AppStore @ObservedObject var viewModel: ViewModel @State private var isShowingEditView = false @@ -64,11 +65,11 @@ extension CardDeckContainerView: View { self.editDeckButton } } + .padding() } + .padding() } } - .padding() - .padding() .background(Color("CardDeckBackground")) .edgesIgnoringSafeArea(.all) .sheet( @@ -82,6 +83,13 @@ extension CardDeckContainerView: View { viewModel: .init(currentDeck: self.viewModel.cardDeck) ) } + .sheet( + isPresented: self.$isShowingSettingsView, + onDismiss: viewModel.resumeRound + ) { + SettingsContainerView() + .environmentObject(self.store) + } .onAppear { self.viewModel.isTimerActive = true } @@ -172,6 +180,7 @@ struct CardDeckContainerView_Previews: PreviewProvider { viewModel: .init(cardDeck: PreviewData.CardDecks.default) ) .environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext) + .environmentObject(PreviewData.AppStores.default) // .previewLayout(PreviewLayout.iPhone11Landscape) } } diff --git a/day-086/Projects/Flashzilla/Flashzilla/Scenes/RootView.swift b/day-086/Projects/Flashzilla/Flashzilla/Scenes/RootView.swift index 996d3c7..37fdb19 100644 --- a/day-086/Projects/Flashzilla/Flashzilla/Scenes/RootView.swift +++ b/day-086/Projects/Flashzilla/Flashzilla/Scenes/RootView.swift @@ -49,5 +49,6 @@ struct RootView_Previews: PreviewProvider { static var previews: some View { RootView() .environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext) + .environmentObject(PreviewData.AppStores.default) } } diff --git a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsContainerView.swift b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsContainerView.swift new file mode 100644 index 0000000..99491c7 --- /dev/null +++ b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsContainerView.swift @@ -0,0 +1,46 @@ +// +// SettingsContainerView.swift +// Flashzilla +// +// Created by CypherPoet on 1/22/20. +// ✌️ +// + +import SwiftUI + + +struct SettingsContainerView: View { +} + + +// MARK: - Body +extension SettingsContainerView { + + var body: some View { + NavigationView { + SettingsFormView() + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} + + +// MARK: - Computeds +extension SettingsContainerView { +} + + +// MARK: - View Variables +extension SettingsContainerView { +} + + + +// MARK: - Preview +struct SettingsContainerView_Previews: PreviewProvider { + + static var previews: some View { + SettingsContainerView() + .environmentObject(PreviewData.AppStores.default) + } +} diff --git a/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsFormView.swift b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsFormView.swift new file mode 100644 index 0000000..e9c6eb6 --- /dev/null +++ b/day-086/Projects/Flashzilla/Flashzilla/Scenes/Settings/SettingsFormView.swift @@ -0,0 +1,74 @@ +// +// SettingsFormView.swift +// Flashzilla +// +// Created by CypherPoet on 1/22/20. +// ✌️ +// + +import SwiftUI + + +struct SettingsFormView { + @Environment(\.presentationMode) private var presentationMode + @EnvironmentObject var store: AppStore + + + private var restacksIncorrectCards: Binding { + store.binding(for: \.settingsState.restacksIncorrectCards) { + .settings(.restacksIncorrectCardsSet($0)) + } + } +} + + +// MARK: - View +extension SettingsFormView: View { + + var body: some View { + Form { + Section { + Toggle(isOn: restacksIncorrectCards) { + Text("Restack After Wrong Answers") + } + } + } + .navigationBarItems(trailing: doneButton) + .navigationBarTitle("Settings") + } +} + + +// MARK: - Computeds +extension SettingsFormView { + var settingsState: SettingsState { store.state.settingsState } +} + + +// MARK: - View Variables +extension SettingsFormView { + + private var doneButton: some View { + Button(action: { + self.presentationMode.wrappedValue.dismiss() + }) { + Text("Done") + } + } +} + + +// MARK: - Private Helpers +private extension SettingsFormView { +} + + + +// MARK: - Preview +struct SettingsFormView_Previews: PreviewProvider { + + static var previews: some View { + SettingsFormView() + .environmentObject(PreviewData.AppStores.default) + } +}