diff --git a/Modules/GameEngineKit/Resources/Localizable.xcstrings b/Modules/GameEngineKit/Resources/Localizable.xcstrings index 1a1b5feb62..f80e6d288a 100644 --- a/Modules/GameEngineKit/Resources/Localizable.xcstrings +++ b/Modules/GameEngineKit/Resources/Localizable.xcstrings @@ -56,6 +56,24 @@ } } }, + "game_engine_kit.activity_view.finish_button": { + "comment": "The title of the finish button", + "extractionState": "extracted_with_value", + "localizations": { + "en": { + "stringUnit": { + "state": "new", + "value": "Finish" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Terminer" + } + } + } + }, "game_engine_kit.activity_view.hide_reinforcer_to_show_answers_button": { "comment": "The title of the hide reinforcer to show answers button", "extractionState": "extracted_with_value", diff --git a/Modules/GameEngineKit/Sources/Exercises/ExerciseState.swift b/Modules/GameEngineKit/Sources/Exercises/ExerciseState.swift index a924aeb18b..1812ca5f1a 100644 --- a/Modules/GameEngineKit/Sources/Exercises/ExerciseState.swift +++ b/Modules/GameEngineKit/Sources/Exercises/ExerciseState.swift @@ -6,11 +6,16 @@ import AccountKit enum ExerciseState: Equatable { case idle - case playing + case playing(type: PlayType = .structured) case completed(level: CompletionLevel) // MARK: Internal + enum PlayType { + case structured + case unstructured + } + enum CompletionLevel { case fail case belowAverage diff --git a/Modules/GameEngineKit/Sources/Exercises/Specialized/DanceFreeze/DanceFreezeView+ViewModel.swift b/Modules/GameEngineKit/Sources/Exercises/Specialized/DanceFreeze/DanceFreezeView+ViewModel.swift index 7e7917ea3f..f73b1f4ce1 100644 --- a/Modules/GameEngineKit/Sources/Exercises/Specialized/DanceFreeze/DanceFreezeView+ViewModel.swift +++ b/Modules/GameEngineKit/Sources/Exercises/Specialized/DanceFreeze/DanceFreezeView+ViewModel.swift @@ -18,7 +18,7 @@ extension DanceFreezeView { self.motionMode = motion self.exercicesSharedData = shared ?? ExerciseSharedData() - self.exercicesSharedData.state = .playing + self.exercicesSharedData.state = .playing() self.subscribeToAudioPlayerProgress() } diff --git a/Modules/GameEngineKit/Sources/Exercises/Specialized/HideAndSeek/HideAndSeekView+Player.swift b/Modules/GameEngineKit/Sources/Exercises/Specialized/HideAndSeek/HideAndSeekView+Player.swift index 8a40ae8f31..ba3a68ddeb 100644 --- a/Modules/GameEngineKit/Sources/Exercises/Specialized/HideAndSeek/HideAndSeekView+Player.swift +++ b/Modules/GameEngineKit/Sources/Exercises/Specialized/HideAndSeek/HideAndSeekView+Player.swift @@ -15,7 +15,7 @@ extension HideAndSeekView { _stage = stage self.exercicesSharedData = shared ?? ExerciseSharedData() - self.exercicesSharedData.state = .playing + self.exercicesSharedData.state = .playing() } // MARK: Internal diff --git a/Modules/GameEngineKit/Sources/Exercises/Specialized/Melody/MelodyView+ViewModel.swift b/Modules/GameEngineKit/Sources/Exercises/Specialized/Melody/MelodyView+ViewModel.swift index de19c685b5..ec9b578192 100644 --- a/Modules/GameEngineKit/Sources/Exercises/Specialized/Melody/MelodyView+ViewModel.swift +++ b/Modules/GameEngineKit/Sources/Exercises/Specialized/Melody/MelodyView+ViewModel.swift @@ -15,7 +15,7 @@ extension MelodyView { self.midiPlayer = midiPlayer self.defaultScale = selectedSong.song.scale self.exercicesSharedData = shared ?? ExerciseSharedData() - self.exercicesSharedData.state = .playing + self.exercicesSharedData.state = .playing() self.setMIDIRecording(midiRecording: selectedSong) } diff --git a/Modules/GameEngineKit/Sources/Exercises/Specialized/SuperSimon/GameplaySuperSimon.swift b/Modules/GameEngineKit/Sources/Exercises/Specialized/SuperSimon/GameplaySuperSimon.swift index 3315052a8e..466a94c0eb 100644 --- a/Modules/GameEngineKit/Sources/Exercises/Specialized/SuperSimon/GameplaySuperSimon.swift +++ b/Modules/GameEngineKit/Sources/Exercises/Specialized/SuperSimon/GameplaySuperSimon.swift @@ -68,7 +68,7 @@ class GameplaySuperSimon: StatefulGameplayProtocol { self.allowedTrials = self.getNumberOfAllowedTrials(from: kGradingLUTDefault) - self.state.send(.playing) + self.state.send(.playing()) } // MARK: Internal diff --git a/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+DragAndDrop.swift b/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+DragAndDrop.swift index e3c413e918..d8a44d4e6a 100644 --- a/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+DragAndDrop.swift +++ b/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+DragAndDrop.swift @@ -28,7 +28,7 @@ extension GameplayAssociateCategories where ChoiceModelType == GameplayAssociate convenience init(choices: [GameplayAssociateCategoriesChoiceModel], allowedTrials: Int? = nil) { self.init() self.choices.send(choices) - self.state.send(.playing) + self.state.send(.playing()) if let allowedTrials { self.allowedTrials = allowedTrials diff --git a/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+Memory.swift b/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+Memory.swift index 389079a949..186720f0fa 100644 --- a/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+Memory.swift +++ b/Modules/GameEngineKit/Sources/Gameplays/AssociateCategories/GameplayAssociateCategories+Memory.swift @@ -28,7 +28,7 @@ extension GameplayAssociateCategories where ChoiceModelType == GameplayMemoryCho convenience init(choices: [GameplayMemoryChoiceModel], allowedTrials: Int? = nil) { self.init() self.choices.send(choices) - self.state.send(.playing) + self.state.send(.playing()) if let allowedTrials { self.allowedTrials = allowedTrials diff --git a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+DragAndDropIntoZones.swift b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+DragAndDropIntoZones.swift index e8cb2e985a..ae9546df84 100644 --- a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+DragAndDropIntoZones.swift +++ b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+DragAndDropIntoZones.swift @@ -29,7 +29,7 @@ extension GameplayFindTheRightAnswers where ChoiceModelType == GameplayDragAndDr self.init() self.choices.send(choices) self.rightAnswers = choices.filter { $0.choice.dropZone != .none } - self.state.send(.playing) + self.state.send(.playing()) if let allowedTrials { self.allowedTrials = allowedTrials diff --git a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+TouchToSelect.swift b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+TouchToSelect.swift index 2bb46a1bbc..8d100c0b1f 100644 --- a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+TouchToSelect.swift +++ b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightAnswers/GameplayFindTheRightAnswers+TouchToSelect.swift @@ -29,7 +29,7 @@ extension GameplayFindTheRightAnswers where ChoiceModelType == GameplayTouchToSe self.init() self.choices.send(shuffle ? choices.shuffled() : choices) self.rightAnswers = choices.filter(\.choice.isRightAnswer) - self.state.send(.playing) + self.state.send(.playing()) if let allowedTrials { self.allowedTrials = allowedTrials diff --git a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightOrder/GameplayFindTheRightOrder+DragAndDropInOrder.swift b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightOrder/GameplayFindTheRightOrder+DragAndDropInOrder.swift index 27d46052d5..bf952cb9f1 100644 --- a/Modules/GameEngineKit/Sources/Gameplays/FindTheRightOrder/GameplayFindTheRightOrder+DragAndDropInOrder.swift +++ b/Modules/GameEngineKit/Sources/Gameplays/FindTheRightOrder/GameplayFindTheRightOrder+DragAndDropInOrder.swift @@ -29,7 +29,7 @@ extension GameplayFindTheRightOrder where ChoiceModelType == GameplayDragAndDrop self.init() self.rightAnswers = choices self.choices.send(choices) - self.state.send(.playing) + self.state.send(.playing()) if let allowedTrials { self.allowedTrials = allowedTrials diff --git a/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift b/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift index ed9a49be29..2dd61f498f 100644 --- a/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift +++ b/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift @@ -229,6 +229,22 @@ public struct ActivityView: View { ) } + private var finishButton: some View { + Button(String(l10n.GameEngineKit.ActivityView.finishButton.characters)) { + // TODO(@macteuts): Move the following out of then View if relevant + self.viewModel.currentExerciseSharedData.state = .completed(level: .excellent) + } + .buttonStyle(.bordered) + .tint(.gray) + .padding() + .transition( + .asymmetric( + insertion: .opacity.animation(.snappy.delay(2)), + removal: .identity + ) + ) + } + private var hideReinforcerToShowAnswersButton: some View { Button(String(l10n.GameEngineKit.ActivityView.hideReinforcerToShowAnswersButton.characters)) { withAnimation { diff --git a/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift+l10n.swift b/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift+l10n.swift index bd8d53a679..d1961d9901 100644 --- a/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift+l10n.swift +++ b/Modules/GameEngineKit/Sources/Views/Activity/ActivityView.swift+l10n.swift @@ -49,6 +49,13 @@ extension l10n { comment: "The title of the continue button" ) + static let finishButton = LocalizedString( + "game_engine_kit.activity_view.finish_button", + bundle: GameEngineKitResources.bundle, + value: "Finish", + comment: "The title of the finish button" + ) + static let hideReinforcerToShowAnswersButton = LocalizedString( "game_engine_kit.activity_view.hide_reinforcer_to_show_answers_button", bundle: GameEngineKitResources.bundle,