Skip to content

Commit 2f9a546

Browse files
authored
Merge pull request #291 from TaskarCenterAtUW/task-2367-longform-3-0-0
Task 2367 longform 3 0 0
2 parents dbe8ef5 + 2ff802a commit 2f9a546

File tree

7 files changed

+204
-30
lines changed

7 files changed

+204
-30
lines changed

GoInfoGame/GoInfoGame.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2918,7 +2918,7 @@
29182918
"$(inherited)",
29192919
"@executable_path/Frameworks",
29202920
);
2921-
MARKETING_VERSION = 1.0.1;
2921+
MARKETING_VERSION = 1.0.2;
29222922
OTHER_LDFLAGS = (
29232923
"$(inherited)",
29242924
"-l\"c++\"",
@@ -2982,7 +2982,7 @@
29822982
"$(inherited)",
29832983
"@executable_path/Frameworks",
29842984
);
2985-
MARKETING_VERSION = 1.0.1;
2985+
MARKETING_VERSION = 1.0.2;
29862986
OTHER_LDFLAGS = (
29872987
"$(inherited)",
29882988
"-l\"c++\"",

GoInfoGame/GoInfoGame/quests/LongQuests/Components/QuestOptions.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ struct QuestOptions: View {
3535
NumericInputView(
3636
selectedChoice: $selectedChoice
3737
)
38+
case .textEntry:
39+
TextEntryView(
40+
selectedChoice: $selectedChoice
41+
)
3842
}
3943
}
4044
}
@@ -217,6 +221,41 @@ private extension QuestOptions {
217221
}
218222
}
219223
}
224+
225+
struct TextEntryView: View {
226+
@Binding var selectedChoice: QuestAnswerChoice?
227+
let maxLenth: Int = 255
228+
229+
private var currentValue: String {
230+
selectedChoice?.value ?? ""
231+
}
232+
233+
var body: some View {
234+
ZStack(alignment: .bottomTrailing) {
235+
TextEditor(text: Binding(
236+
get: { selectedChoice?.value ?? "" },
237+
set: { newValue in
238+
let truncatedValue = String(newValue.prefix(maxLenth))
239+
if truncatedValue.isEmpty {
240+
selectedChoice = nil
241+
} else {
242+
let answer = QuestAnswerChoice(value: truncatedValue, choiceText: truncatedValue, imageURL: nil, choiceFollowUp: nil)
243+
selectedChoice = answer
244+
}
245+
}
246+
))
247+
.frame(height: 100)
248+
.keyboardType(UIKeyboardType.default)
249+
.border(Color.gray)
250+
251+
Text("\(currentValue.count) / \(maxLenth)")
252+
.font(.caption)
253+
.foregroundColor(.gray)
254+
.padding(5)
255+
.background(Color.white.opacity(0.5))
256+
}
257+
}
258+
}
220259

221260
struct ExpandedImageView: View {
222261
let imageUrl: String

GoInfoGame/GoInfoGame/quests/LongQuests/Model/LongFormModel.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct LongQuest: Codable, Identifiable {
6969
var questAnswerChoices: [QuestAnswerChoice]?
7070
var questImageURL: String?
7171
var questAnswerValidation: QuestAnswerValidation?
72-
var questAnswerDependency: QuestAnswerDependency?
72+
var questAnswerDependency: [QuestAnswerDependency]?
7373
var questUserAnswer : String?
7474

7575
func getFormValue() ->[String:String?] {
@@ -87,6 +87,27 @@ struct LongQuest: Codable, Identifiable {
8787
case questAnswerValidation = "quest_answer_validation"
8888
case questAnswerDependency = "quest_answer_dependency"
8989
}
90+
91+
init(from decoder: Decoder) throws {
92+
let container = try decoder.container(keyedBy: CodingKeys.self)
93+
questID = try container.decode(Int.self, forKey: .questID)
94+
questTitle = try container.decode(String.self, forKey: .questTitle)
95+
questDescription = try container.decode(String.self, forKey: .questDescription)
96+
questType = try container.decode(QuestType.self, forKey: .questType)
97+
questTag = try container.decode(String.self, forKey: .questTag)
98+
questAnswerChoices = try container.decodeIfPresent([QuestAnswerChoice].self, forKey: .questAnswerChoices)
99+
questImageURL = try container.decodeIfPresent(String.self, forKey: .questImageURL)
100+
questAnswerValidation = try container.decodeIfPresent(QuestAnswerValidation.self, forKey: .questAnswerValidation)
101+
102+
// Handle questAnswerDependency which can be an object or an array
103+
if let singleDependency = try? container.decodeIfPresent(QuestAnswerDependency.self, forKey: .questAnswerDependency) {
104+
questAnswerDependency = [singleDependency]
105+
} else if let multipleDependencies = try? container.decodeIfPresent([QuestAnswerDependency].self, forKey: .questAnswerDependency) {
106+
questAnswerDependency = multipleDependencies
107+
} else {
108+
questAnswerDependency = nil
109+
}
110+
}
90111

91112
func getQtype() -> QuestType {
92113
if (self.questType == .exclusiveChoice) {
@@ -136,6 +157,7 @@ enum QuestType: String, Codable {
136157
case exclusiveChoice = "ExclusiveChoice"
137158
case numeric = "Numeric"
138159
case multipleChoice = "MultipleChoice"
160+
case textEntry = "TextEntry"
139161
// case excWithImg = "ExclusiveChoiceWithImg"
140162
}
141163

GoInfoGame/GoInfoGame/quests/LongQuests/View/LongForm.swift

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ struct LongForm: View, QuestForm {
161161

162162
VStack {
163163
List {
164-
if let quests = questsForLongForm() {
164+
if let quests = questsForLongForm()?.quests {
165165
ForEach(quests, id: \.questID) { quest in
166166
if viewModel.shouldShowQuest(quest) {
167167
LongQuestView(quest: quest, selectedChoice: binding(for: quest), uploadPhoto: { result in
@@ -324,8 +324,10 @@ struct LongForm: View, QuestForm {
324324
})
325325
}
326326

327-
func questsForLongForm() -> [LongQuest]? {
328-
return QuestsRepository.shared.questsForQuery(query ?? "")
327+
func questsForLongForm() -> LongFormElement? {
328+
let element = QuestsRepository.shared.questElementForQuery(query ?? "")
329+
viewModel.longForm = element
330+
return element
329331
}
330332

331333
private func binding(for quest: LongQuest) -> Binding<QuestAnswerChoice?> {
@@ -339,13 +341,110 @@ struct LongForm: View, QuestForm {
339341
}
340342

341343
#Preview {
342-
let quest = LongFormElement(elementType: "Sidewalks", questQuery: "ways with (highway=footway and footway=sidewalk)", elementTypeIcon: "icon", quests: [LongQuest(questID: 14,
343-
questTitle: "Does the length of this crossing allow for safe navigation?",
344-
questDescription: "Determine whether this crossing is short enough to cross safely.",
345-
questType: .exclusiveChoice,
346-
questTag: "ext:crossing_adequate_length",
347-
questAnswerChoices: [QuestAnswerChoice(value: "yes", choiceText: "Yes, this roadway can be crossed safely. this is to test the line limit functionlity. want to see the max capability of this feature. the max lines should be 10. this is for our obervations only. till now it is able to render 10 lines with out any issue.", imageURL: nil, choiceFollowUp: nil),
348-
QuestAnswerChoice(value: "no", choiceText: "No, this roadway is too wide to cross safely.", imageURL: nil, choiceFollowUp: nil)], questImageURL: nil, questAnswerValidation: nil, questAnswerDependency: nil, questUserAnswer: nil)])
344+
let jsonString = """
345+
{
346+
"element_type": "Crossings",
347+
"element_type_icon": "pedestrian_crossing",
348+
"quest_query": "ways with (highway=footway and footway=crossing)",
349+
"quests": [
350+
{
351+
"quest_id": 201,
352+
"quest_title": "Does this crossing have markings on the roadway?",
353+
"quest_description": "Check if there are roadway markings present at this crossing.",
354+
"quest_type": "ExclusiveChoice",
355+
"quest_tag": "crossing:markings",
356+
"quest_answer_choices": [
357+
{
358+
"value": "no",
359+
"choice_text": "No",
360+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/markings/no_square.png"
361+
},
362+
{
363+
"value": "yes",
364+
"choice_text": "Yes",
365+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/markings/zebra_square.png"
366+
}
367+
]
368+
},
369+
{
370+
"quest_id": 202,
371+
"quest_title": "Does this crossing have signals for pedestrians?",
372+
"quest_description": "Indicate whether this crossing has pedestrian signals.",
373+
"quest_type": "ExclusiveChoice",
374+
"quest_tag": "ext:crossing:signals",
375+
"quest_answer_choices": [
376+
{
377+
"value": "no",
378+
"choice_text": "No",
379+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/markings/no_2_square.png"
380+
},
381+
{
382+
"value": "yes",
383+
"choice_text": "Yes",
384+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/signals/arrow/yes_square.png"
385+
}
386+
]
387+
},
388+
{
389+
"quest_id": 203,
390+
"quest_title": "What accessibility features are present at this signalized crossing?",
391+
"quest_description": "Select all accessibility features present at this signalized crossing.",
392+
"quest_type": "MultipleChoice",
393+
"quest_tag": "ext:crossing:signals:features",
394+
"quest_answer_dependency": [
395+
{
396+
"question_id": 202,
397+
"required_value": "yes"
398+
}
399+
],
400+
"quest_answer_choices": [
401+
{
402+
"value": "button",
403+
"choice_text": "Button",
404+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/signals/arrow/yes_square.png"
405+
},
406+
{
407+
"value": "arrow",
408+
"choice_text": "Tactile Arrow",
409+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/signals/arrow/yes_square.png"
410+
},
411+
{
412+
"value": "sound",
413+
"choice_text": "Sound",
414+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/signals/sound/yes_square.png"
415+
},
416+
{
417+
"value": "vibration",
418+
"choice_text": "Tactile Vibration",
419+
"image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/crossing/signals/vibration/yes_square.png"
420+
}
421+
]
422+
},
423+
{
424+
"quest_id": 204,
425+
"quest_title": "How many lanes are crossed at this crossing?",
426+
"quest_description": "Count the total number of traffic lanes crossed at this crossing.",
427+
"quest_type": "Numeric",
428+
"quest_tag": "ext:crossing:count_lanes_crossed",
429+
"quest_answer_validation": {
430+
"min": 1,
431+
"max": 10
432+
}
433+
},
434+
{
435+
"quest_id": 205,
436+
"quest_title": "Additional crossing notes...",
437+
"quest_description": "Add any additional observations you'd like to record about this crossing",
438+
"quest_type": "TextEntry",
439+
"quest_tag": "ext:crossing:description"
440+
}
441+
]
442+
}
443+
"""
444+
445+
guard let quest = try? JSONDecoder().decode(LongFormElement.self, from: jsonString.data(using: .utf8)!) else {
446+
return Text("Error parsing JSON")
447+
}
349448
QuestsRepository.shared.longQuestModels.append(quest)
350449
return LongForm(elementName: quest.elementType, questID: "questId",query: quest.questQuery, action: { tags in
351450
})

GoInfoGame/GoInfoGame/quests/LongQuests/View/LongQuestView.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,19 @@ struct LongQuestView: View {
5454
}
5555

5656
#Preview {
57-
let longQeust = LongQuest(questID: 14,
58-
questTitle: "Does the length of this crossing allow for safe navigation?",
59-
questDescription: "Determine whether this crossing is short enough to cross safely.",
60-
questType: .exclusiveChoice,
61-
questTag: "ext:crossing_adequate_length",
62-
questAnswerChoices: [QuestAnswerChoice(value: "yes", choiceText: "Yes, this roadway can be crossed safely.", imageURL: nil, choiceFollowUp: nil),
63-
QuestAnswerChoice(value: "no", choiceText: "No, this roadway is too wide to cross safely.", imageURL: nil, choiceFollowUp: nil)], questImageURL: nil, questAnswerValidation: nil, questAnswerDependency: nil, questUserAnswer: nil)
64-
65-
66-
LongQuestView(quest: longQeust, selectedChoice: .constant(nil), uploadPhoto: { s in
57+
let jsonString = """
58+
{
59+
"quest_id": 205,
60+
"quest_title": "Additional crossing notes...",
61+
"quest_description": "Add any additional observations you'd like to record about this crossing",
62+
"quest_type": "TextEntry",
63+
"quest_tag": "ext:crossing:description",
64+
"quest_image_url": "https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/images/kerb/lowered_landscape.png"
65+
}
66+
"""
67+
if let longQeust = try? JSONDecoder().decode(LongQuest.self, from: jsonString.data(using: .utf8)!) {
68+
LongQuestView(quest: longQeust, selectedChoice: .constant(nil), uploadPhoto: { s in
6769

68-
})
70+
})
71+
}
6972
}

GoInfoGame/GoInfoGame/quests/LongQuests/ViewModel/LongFormViewModel.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,26 @@
88
import Foundation
99

1010
class LongFormViewModel: ObservableObject {
11-
@Published var longForm: LongFormElement?
11+
var longForm: LongFormElement?
1212
@Published var selectedChoices: [Int: QuestAnswerChoice?] = [:]
1313

1414
init() {}
1515

1616
func shouldShowQuest(_ quest: LongQuest) -> Bool {
17-
guard let dependency = quest.questAnswerDependency else {
18-
return true
19-
}
17+
guard let dependencies = quest.questAnswerDependency else {
18+
return true
19+
}
20+
var dependencyResult: Bool = true
21+
for dependency in dependencies {
22+
dependencyResult = dependencyResult && checkDependency(dependency)
23+
if !dependencyResult {
24+
return dependencyResult
25+
}
26+
}
27+
return dependencyResult
28+
}
29+
30+
func checkDependency(_ dependency: QuestAnswerDependency) -> Bool {
2031
if let answeredChoiceOptional = selectedChoices[dependency.questionID],
2132
let answeredChoice = answeredChoiceOptional,
2233
!answeredChoice.value.isEmpty {

GoInfoGame/GoInfoGame/quests/QuestsRepository.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ extension QuestsRepository {
148148
return longQuestModels.first(where: { $0.elementType.lowercased() == elementType.lowercased() })?.questQuery
149149
}
150150

151-
func questsForQuery(_ query: String) -> [LongQuest]? {
152-
return longQuestModels.first(where: {$0.questQuery == query})?.quests
151+
func questElementForQuery(_ query: String) -> LongFormElement? {
152+
return longQuestModels.first(where: {$0.questQuery == query})
153153
}
154154
}
155155

0 commit comments

Comments
 (0)