Skip to content

Commit

Permalink
🔀️ Merge branch 'hugo/feature/Add-ActionThenDnD-to-NewGEK' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
ladislas committed Dec 17, 2024
2 parents 0968333 + 0bd2e74 commit 9c164ab
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,80 @@ struct ContentView: View {

Spacer()
}

HStack(spacing: 20) {
Text("Action Then DnDGrid")
.font(.title)
.padding()

NavigationLink("Observe Image Then Drag & Drop Categories", destination: {
let gameplay = NewGameplayAssociateCategories(choices: NewGameplayAssociateCategories.kDefaultChoices)
let coordinator = DnDGridCoordinatorAssociateCategories(gameplay: gameplay,
action: .ipad(type: .image("sport_dance_player_man")))
let viewModel = DnDGridViewModel(coordinator: coordinator)

return DnDGridView(viewModel: viewModel)
.navigationTitle("Observe Image Then Drag & Drop Categories")
.navigationBarTitleDisplayMode(.large)
})
.tint(.pink)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

NavigationLink("Observe SFSymbol Then Drag & Drop Categories", destination: {
let gameplay = NewGameplayAssociateCategories(choices: NewGameplayAssociateCategories.kDefaultChoices)
let coordinator = DnDGridCoordinatorAssociateCategories(gameplay: gameplay, action: .ipad(type: .sfsymbol("star")))
let viewModel = DnDGridViewModel(coordinator: coordinator)

return DnDGridView(viewModel: viewModel)
.navigationTitle("Observe SFSymbol Then Drag & Drop Categories")
.navigationBarTitleDisplayMode(.large)
})
.tint(.pink)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

NavigationLink("Listen Then Drag & Drop Categories", destination: {
let gameplay = NewGameplayAssociateCategories(choices: NewGameplayAssociateCategories.kDefaultChoices)
let coordinator = DnDGridCoordinatorAssociateCategories(gameplay: gameplay, action: .ipad(type: .audio("sound_animal_duck")))
let viewModel = DnDGridViewModel(coordinator: coordinator)

return DnDGridView(viewModel: viewModel)
.navigationTitle("Listen Then Drag & Drop Categories")
.navigationBarTitleDisplayMode(.large)
})
.tint(.pink)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

NavigationLink("Listen Speech Then Drag & Drop Categories", destination: {
let gameplay = NewGameplayAssociateCategories(choices: NewGameplayAssociateCategories.kDefaultImageChoices)
let coordinator = DnDGridCoordinatorAssociateCategories(gameplay: gameplay, action: .ipad(type: .speech("Correct answer")))
let viewModel = DnDGridViewModel(coordinator: coordinator)

return DnDGridView(viewModel: viewModel)
.navigationTitle("Listen Speech Then Drag & Drop Categories")
.navigationBarTitleDisplayMode(.large)
})
.tint(.pink)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

NavigationLink("Robot Then Drag & Drop Categories", destination: {
let gameplay = NewGameplayAssociateCategories(choices: NewGameplayAssociateCategories.kDefaultChoices)
let coordinator = DnDGridCoordinatorAssociateCategories(gameplay: gameplay, action: .robot(type: .color("red")))
let viewModel = DnDGridViewModel(coordinator: coordinator)

return DnDGridView(viewModel: viewModel)
.navigationTitle("Listen Speech Then Drag & Drop Categories")
.navigationBarTitleDisplayMode(.large)
})
.tint(.pink)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)

Spacer()
}
}

Text("Or choose a template")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ public extension NewGameplayAssociateCategories {
NewGameplayAssociateCategoriesChoice(value: "Maison", category: nil, type: .text),
]

static let kDefaultImageChoices: [NewGameplayAssociateCategoriesChoice] = [
NewGameplayAssociateCategoriesChoice(value: "pictograms-weather-sun_yellow-0106", category: .categoryA, type: .image),
NewGameplayAssociateCategoriesChoice(value: "pictograms-animals-arctic-penguin_yellow-0088", category: .categoryB, type: .image),
NewGameplayAssociateCategoriesChoice(value: "pictograms-weather-sun_yellow-0106", category: .categoryA, type: .image),
NewGameplayAssociateCategoriesChoice(value: "pictograms-animals-arctic-penguin_yellow-0088", category: .categoryB, type: .image),
NewGameplayAssociateCategoriesChoice(value: "pictograms-weather-sun_yellow-0106", category: .categoryA, type: .image),
NewGameplayAssociateCategoriesChoice(value: "Maison", category: nil, type: .text),
]

static let kDefaultChoicesWithZones: [NewGameplayAssociateCategoriesChoice] = [
NewGameplayAssociateCategoriesChoice(value: "sun", category: .categoryA, type: .text),
NewGameplayAssociateCategoriesChoice(value: "car", category: .categoryB, type: .text),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

import Combine
import ContentKit
import SpriteKit
import SwiftUI
import UtilsKit
Expand All @@ -12,17 +13,18 @@ import UtilsKit
public class DnDGridCoordinatorAssociateCategories: DnDGridGameplayCoordinatorProtocol {
// MARK: Lifecycle

public init(gameplay: NewGameplayAssociateCategories) {
public init(gameplay: NewGameplayAssociateCategories, action: Exercise.Action? = nil) {
self.gameplay = gameplay

self.uiChoices.value.choices = gameplay.choices.map { choice in
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiChoices.value.choiceSize(for: gameplay.choices.count))
self.uiModel.value.action = action
self.uiModel.value.choices = gameplay.choices.map { choice in
DnDAnswerNode(id: choice.id, value: choice.value, type: choice.type, size: self.uiModel.value.choiceSize(for: gameplay.choices.count))
}
}

// MARK: Public

public private(set) var uiChoices = CurrentValueSubject<DnDUIChoices, Never>(.zero)
public private(set) var uiModel = CurrentValueSubject<DnDGridUIModel, Never>(.zero)

public func onTouch(_ event: DnDTouchEvent, choice: DnDAnswerNode, destination: DnDAnswerNode? = nil) {
switch event {
Expand Down Expand Up @@ -76,7 +78,7 @@ public class DnDGridCoordinatorAssociateCategories: DnDGridGameplayCoordinatorPr
private func updateChoiceState(for choice: NewGameplayAssociateCategoriesChoice, to state: State) {
guard let index = self.gameplay.choices.firstIndex(where: { $0.id == choice.id }) else { return }

self.updateUINodeState(node: self.uiChoices.value.choices[index], state: state)
self.updateUINodeState(node: self.uiModel.value.choices[index], state: state)
}

private func choiceAlreadySelected(choice: NewGameplayAssociateCategoriesChoice) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import Combine
import Foundation

public protocol DnDGridGameplayCoordinatorProtocol {
var uiChoices: CurrentValueSubject<DnDUIChoices, Never> { get }
var uiModel: CurrentValueSubject<DnDGridUIModel, Never> { get }
func onTouch(_ event: DnDTouchEvent, choice: DnDAnswerNode, destination: DnDAnswerNode?)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

import ContentKit
import SpriteKit
import SwiftUI

// MARK: - DnDGridViewUIChoicesWrapper

public struct DnDGridUIModel {
static let zero = DnDGridUIModel(action: nil, choices: [])

var action: Exercise.Action?
var choices: [DnDAnswerNode]

// swiftlint:disable cyclomatic_complexity

func choiceSize(for numberOfChoices: Int) -> CGSize {
switch self.action {
case .ipad(type: .image),
.ipad(type: .sfsymbol):
switch numberOfChoices {
case 1...4:
CGSize(width: 180, height: 180)
case 5...6:
CGSize(width: 150, height: 150)
default:
CGSize(width: 150, height: 150)
}
case .none:
switch numberOfChoices {
case 1...2:
CGSize(width: 300, height: 300)
case 3...4:
CGSize(width: 240, height: 240)
case 5...6:
CGSize(width: 200, height: 200)
default:
CGSize(width: 200, height: 200)
}
default:
switch numberOfChoices {
case 1...2:
CGSize(width: 220, height: 220)
case 3...4:
CGSize(width: 200, height: 200)
case 5:
CGSize(width: 160, height: 160)
case 6:
CGSize(width: 150, height: 150)
default:
CGSize(width: 150, height: 150)
}
}
}

// swiftlint:enable cyclomatic_complexity
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,44 @@ public struct DnDGridView: View {
// MARK: Public

public var body: some View {
GeometryReader { proxy in
SpriteView(scene: self.makeScene(size: proxy.size), options: [.allowsTransparency])
.frame(width: proxy.size.width, height: proxy.size.height)
.onAppear {
self.scene = self.getScene(for: self.viewModel.choices.count, size: proxy.size)
HStack(spacing: 0) {
if let action = self.viewModel.action {
Button {
// nothing to do
}
label: {
ActionButtonView(action: action)
.padding(20)
}
.simultaneousGesture(
TapGesture()
.onEnded { _ in
withAnimation {
self.viewModel.isActionTriggered = true
}
}
)

Divider()
.opacity(0.4)
.frame(maxHeight: 500)
.padding(.vertical, 20)
}

Spacer()

GeometryReader { proxy in
SpriteView(scene: self.makeScene(size: proxy.size), options: [.allowsTransparency])
.frame(width: proxy.size.width, height: proxy.size.height)
.onAppear {
self.scene = self.getScene(for: self.viewModel.choices.count, size: proxy.size)
}
}
.colorMultiply(self.viewModel.isActionTriggered ? .white : .gray.opacity(0.4))
.animation(.easeOut(duration: 0.3), value: self.viewModel.isActionTriggered)
.allowsHitTesting(self.viewModel.isActionTriggered)

Spacer()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

import Combine
import ContentKit
import SwiftUI

// MARK: - DnDGridViewModel
Expand All @@ -11,12 +12,14 @@ public class DnDGridViewModel: ObservableObject {
// MARK: Lifecycle

public init(coordinator: DnDGridGameplayCoordinatorProtocol) {
self.choices = coordinator.uiChoices.value.choices
self.choices = coordinator.uiModel.value.choices
self.action = coordinator.uiModel.value.action
self.isActionTriggered = (self.action == nil) ? true : false
self.coordinator = coordinator
self.coordinator.uiChoices
self.coordinator.uiModel
.receive(on: DispatchQueue.main)
.sink { [weak self] choices in
self?.choices = choices.choices
.sink { [weak self] model in
self?.choices = model.choices
}
.store(in: &self.cancellables)
}
Expand All @@ -29,8 +32,11 @@ public class DnDGridViewModel: ObservableObject {

// MARK: Internal

@Published var isActionTriggered = false
@Published var choices: [DnDAnswerNode] = []

let action: Exercise.Action?

// MARK: Private

private let coordinator: DnDGridGameplayCoordinatorProtocol
Expand Down

0 comments on commit 9c164ab

Please sign in to comment.