From 9952560ab9677655b9fed0806482fc94e122eecf Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 15:02:26 +0100 Subject: [PATCH 1/8] :technologist: (git): Pre-commit - pretty-format-json exclude jtd --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0aa842fb0..6dc27d1e9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,10 +13,10 @@ repos: types: [file] files: \.(json|xcstrings)$ - id: pretty-format-json - args: ['--autofix', '--indent=4', '--top-keys=version,sourceLanguage'] + args: ["--autofix", "--indent=4", "--top-keys=version,sourceLanguage"] types: [file] files: \.(json|xcstrings)$ - exclude: '.vscode/settings.json' + exclude: '(.vscode/settings.json|\.jtd.json$)' - id: check-added-large-files - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable From 56a290d1f3dcbb2888bf48852c3a5b8bfbad2cb2 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 14:50:56 +0100 Subject: [PATCH 2/8] :technologist: (swiftlint): identifier_name - exclude hmi and derivatives --- .swiftlint.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.swiftlint.yml b/.swiftlint.yml index 3d11622dc8..bb6e58aac1 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -29,6 +29,10 @@ identifier_name: - BLE - cmd - g + - hmi + - HMI + - hmis + - HMIs - i - i18n - id From efa82d3ee2257b54ac6b1a3671164a13189118d7 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 14:54:29 +0100 Subject: [PATCH 3/8] :sparkles: (ContentKit): HMI - add list, ability to search by id --- .../ContentKitExample/Sources/MainView.swift | 8 ++ .../ContentKit/Resources/Content/hmi/hmi.yml | 49 ++++++++ Modules/ContentKit/Sources/Models/HMI.swift | 108 ++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 Modules/ContentKit/Resources/Content/hmi/hmi.yml create mode 100644 Modules/ContentKit/Sources/Models/HMI.swift diff --git a/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift b/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift index fd38b4d0a0..042ed72b2d 100644 --- a/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift +++ b/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift @@ -119,6 +119,14 @@ struct MainView: View { print("name: \(skill.name)") print("description: \(skill.description)") } + + let hmis = HMI.list + for (index, hmi) in hmis.enumerated() { + print("hmi \(index + 1)") + print("id: \(hmi.id)") + print("name: \(hmi.name)") + print("description: \(hmi.description)") + } } } diff --git a/Modules/ContentKit/Resources/Content/hmi/hmi.yml b/Modules/ContentKit/Resources/Content/hmi/hmi.yml new file mode 100644 index 0000000000..816482afee --- /dev/null +++ b/Modules/ContentKit/Resources/Content/hmi/hmi.yml @@ -0,0 +1,49 @@ +# Leka - iOS Monorepo +# Copyright APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +version: 1.0.0 +list: + - id: robot + l10n: + - locale: fr_FR + name: Robot + description: | + L'utilisateur interagit directement avec le robot. + - locale: en_US + name: Robot + description: | + The user interacts directly with the robot. + + - id: magic_cards + l10n: + - locale: fr_FR + name: Cartes magiques + description: | + L'utilisateur interagit avec le robot en utilisant les cartes magiques. + - locale: en_US + name: Magic cards + description: | + The user interacts with the robot by using magic cards. + + - id: ipad_robot + l10n: + - locale: fr_FR + name: iPad et robot + description: | + L'utilisateur interagit avec l'iPad, le robot agit en tant que support pour observer avant de répondre. + - locale: en_US + name: iPad and robot + description: | + The user interacts with the iPad, and the robot serves as support for observation before providing answers. + + - id: ipad + l10n: + - locale: fr_FR + name: iPad + description: | + L'utilisateur interagit avec l'iPad, le robot agit en tant que renforçateur. + - locale: en_US + name: iPad + description: | + The user interacts with the iPad, and the robot serves as reinforcer. diff --git a/Modules/ContentKit/Sources/Models/HMI.swift b/Modules/ContentKit/Sources/Models/HMI.swift new file mode 100644 index 0000000000..4bfbd4faef --- /dev/null +++ b/Modules/ContentKit/Sources/Models/HMI.swift @@ -0,0 +1,108 @@ +// Leka - iOS Monorepo +// Copyright APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import Foundation +import LocalizationKit +import LogKit +import Version +import Yams + +// MARK: - HMI + +public class HMI: Codable { + // MARK: Lifecycle + + private init() { + self.container = Self.loadHMI() + } + + // MARK: Public + + public static var list: [HMIDetails] { + shared.container.list + } + + public static func hmi(id: String) -> HMIDetails? { + self.list.first(where: { $0.id == id }) + } + + // MARK: Private + + private struct HMIContainer: Codable { + let list: [HMIDetails] + } + + private static let shared: HMI = .init() + + private let container: HMIContainer + + private static func loadHMI() -> HMIContainer { + if let fileURL = Bundle.module.url(forResource: "hmi", withExtension: "yml") { + do { + let yamlString = try String(contentsOf: fileURL, encoding: .utf8) + let container = try YAMLDecoder().decode(HMIContainer.self, from: yamlString) + return container + } catch { + log.error("Failed to read YAML file: \(error)") + return HMIContainer(list: []) + } + } else { + log.error("hmi.yml not found") + return HMIContainer(list: []) + } + } +} + +// MARK: - HMIDetails + +public struct HMIDetails: Codable, Identifiable { + // MARK: Lifecycle + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decode(String.self, forKey: .id) + + self.l10n = try container.decode([HMIDetails.Localization].self, forKey: .l10n) + + let availableLocales = self.l10n.map(\.locale) + + let currentLocale = availableLocales.first(where: { + $0.language.languageCode == LocalizationKit.l10n.language + }) ?? Locale(identifier: "en_US") + + self.name = self.l10n.first(where: { $0.locale == currentLocale })!.name + self.description = self.l10n.first(where: { $0.locale == currentLocale })!.description + } + + // MARK: Public + + public let id: String + public let name: String + public let description: String + + // MARK: Private + + private let l10n: [Localization] +} + +// MARK: HMIDetails.Localization + +public extension HMIDetails { + struct Localization: Codable { + // MARK: Lifecycle + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.locale = try Locale(identifier: container.decode(String.self, forKey: .locale)) + self.name = try container.decode(String.self, forKey: .name) + self.description = try container.decode(String.self, forKey: .description) + } + + // MARK: Internal + + let locale: Locale + let name: String + let description: String + } +} From 5d5224b8cdbfdbd1256164d58bd6811577373bb1 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 15:03:27 +0100 Subject: [PATCH 4/8] :sparkles: (ContentKit): JTD - add hmi to activity, curriculum --- .../ContentKit/Specs/jtd/activity.jtd.json | 25 +++++++++---------- .../ContentKit/Specs/jtd/curriculum.jtd.json | 20 ++++++++------- Modules/ContentKit/Specs/tests/activity.yml | 5 ++++ Modules/ContentKit/Specs/tests/curriculum.yml | 5 ++++ 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Modules/ContentKit/Specs/jtd/activity.jtd.json b/Modules/ContentKit/Specs/jtd/activity.jtd.json index f3fea0e29d..f1ad3022bf 100644 --- a/Modules/ContentKit/Specs/jtd/activity.jtd.json +++ b/Modules/ContentKit/Specs/jtd/activity.jtd.json @@ -19,6 +19,11 @@ "ref": "$skill" } }, + "hmi": { + "elements": { + "ref": "$hmi" + } + }, "tags": { "elements": { "ref": "$tag" @@ -58,17 +63,14 @@ "julie_tuil" ] }, + "$hmi": { + "enum": ["robot", "ipad", "magic_cards", "ipad_robot"] + }, "$locale": { - "enum": [ - "en_US", - "fr_FR" - ] + "enum": ["en_US", "fr_FR"] }, "$status": { - "enum": [ - "draft", - "published" - ] + "enum": ["draft", "published"] }, "$l10n": { "properties": { @@ -156,13 +158,10 @@ ] }, "$exercise/gameplay": { - "enum": [ - "findTheRightAnswers", - "associateCategories" - ] + "enum": ["findTheRightAnswers", "associateCategories"] }, "$exercise/payload": { "type": "string" } } -} \ No newline at end of file +} diff --git a/Modules/ContentKit/Specs/jtd/curriculum.jtd.json b/Modules/ContentKit/Specs/jtd/curriculum.jtd.json index de23854d06..3aa3be7013 100644 --- a/Modules/ContentKit/Specs/jtd/curriculum.jtd.json +++ b/Modules/ContentKit/Specs/jtd/curriculum.jtd.json @@ -19,6 +19,11 @@ "ref": "$skill" } }, + "hmi": { + "elements": { + "ref": "$hmi" + } + }, "tags": { "elements": { "ref": "$tag" @@ -48,17 +53,14 @@ "julie_tuil" ] }, + "$hmi": { + "enum": ["robot", "ipad", "magic_cards", "ipad_robot"] + }, "$status": { - "enum": [ - "draft", - "published" - ] + "enum": ["draft", "published"] }, "$locale": { - "enum": [ - "en_US", - "fr_FR" - ] + "enum": ["en_US", "fr_FR"] }, "$l10n": { "properties": { @@ -96,4 +98,4 @@ "type": "string" } } -} \ No newline at end of file +} diff --git a/Modules/ContentKit/Specs/tests/activity.yml b/Modules/ContentKit/Specs/tests/activity.yml index c3119648d9..1cfeeb6744 100644 --- a/Modules/ContentKit/Specs/tests/activity.yml +++ b/Modules/ContentKit/Specs/tests/activity.yml @@ -16,6 +16,11 @@ skills: - skill_two - skill_three +hmi: + - robot + - magic_cards + - ipad_robot + tags: - tag_one - tag_two diff --git a/Modules/ContentKit/Specs/tests/curriculum.yml b/Modules/ContentKit/Specs/tests/curriculum.yml index cd634f3f2a..515fdd322d 100644 --- a/Modules/ContentKit/Specs/tests/curriculum.yml +++ b/Modules/ContentKit/Specs/tests/curriculum.yml @@ -20,6 +20,11 @@ skills: - skill_two - skill_three +hmi: + - robot + - magic_cards + - ipad_robot + tags: - tag_one - tag_two From 9aec82087c31d0b8b389448bc78204e680a614af Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 15:13:48 +0100 Subject: [PATCH 5/8] :sparkles: (ContentKit): Activity - add HMI --- Modules/ContentKit/Sources/Activity/Activity.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/ContentKit/Sources/Activity/Activity.swift b/Modules/ContentKit/Sources/Activity/Activity.swift index 7024a71119..ccd5130297 100644 --- a/Modules/ContentKit/Sources/Activity/Activity.swift +++ b/Modules/ContentKit/Sources/Activity/Activity.swift @@ -19,6 +19,7 @@ public struct Activity: Codable, Identifiable { self.name = try container.decode(String.self, forKey: .name) self.authors = try container.decode([String].self, forKey: .authors) self.skills = try container.decode([String].self, forKey: .skills) + self.hmi = try container.decode([String].self, forKey: .hmi) self.tags = try container.decode([String].self, forKey: .tags) self.status = try container.decode(Status.self, forKey: .status) self.gameengine = try container.decode(GameEngine.self, forKey: .gameengine) @@ -39,6 +40,7 @@ public struct Activity: Codable, Identifiable { public let authors: [String] // TODO: (@ladislas) - implement authors public let skills: [String] // TODO: (@ladislas) - implement skills + public let hmi: [String] // TODO: (@ladislas) - implement hmi public let tags: [String] // TODO: (@ladislas) - implement tags public let locales: [Locale] @@ -71,6 +73,7 @@ public struct Activity: Codable, Identifiable { case name case authors case skills + case hmi case tags case status case locales From 3b9f42e1bba9426b9b393aad153277429dd7d7a2 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 15:14:36 +0100 Subject: [PATCH 6/8] :alembic: (ContentKit): Example - add real hmi and get details --- .../ContentKitExample/Resources/activity.yml | 5 +++++ .../ContentKitExample/Sources/MainView.swift | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml b/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml index ce582ce4d7..e688d403de 100644 --- a/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml +++ b/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml @@ -25,6 +25,11 @@ locales: - en_US - fr_FR +hmi: + - robot + - magic_cards + - ipad_robot + l10n: - locale: fr_FR details: diff --git a/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift b/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift index 042ed72b2d..d985a152ae 100644 --- a/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift +++ b/Modules/ContentKit/Examples/ContentKitExample/Sources/MainView.swift @@ -83,6 +83,20 @@ struct MainView: View { } } + DisclosureGroup("**HMI**") { + ForEach(self.activity?.hmi ?? [], id: \.self) { hmi in + let hmi = HMI.hmi(id: hmi)! + HStack { + Text(hmi.name) + Button { + self.selectedHMI = hmi + } label: { + Image(systemName: "info.circle") + } + } + } + } + DisclosureGroup("**Tags**") { ForEach(self.activity?.tags ?? [], id: \.self) { skill in Text(skill) @@ -108,6 +122,13 @@ struct MainView: View { Text(skill.description) } }) + .sheet(item: self.$selectedHMI, onDismiss: { self.selectedHMI = nil }, content: { hmi in + VStack(alignment: .leading) { + Text(hmi.name) + .font(.headline) + Text(hmi.description) + } + }) .onAppear { self.activity = ContentKit.decodeActivity("activity") print(self.activity ?? "not working") @@ -133,6 +154,7 @@ struct MainView: View { // MARK: Private @State private var selectedSkill: Skill? + @State private var selectedHMI: HMIDetails? @State private var activity: Activity? } From 7a284d8b14983d8af5a2fdab5eb2f157a64a1538 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 16:48:24 +0100 Subject: [PATCH 7/8] :recycle: (ContentKit): HMI - rename ipad to tablet --- .../Examples/ContentKitExample/Resources/activity.yml | 2 +- Modules/ContentKit/Resources/Content/hmi/hmi.yml | 4 ++-- Modules/ContentKit/Specs/jtd/activity.jtd.json | 2 +- Modules/ContentKit/Specs/jtd/curriculum.jtd.json | 2 +- Modules/ContentKit/Specs/tests/activity.yml | 2 +- Modules/ContentKit/Specs/tests/curriculum.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml b/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml index e688d403de..2a865b85df 100644 --- a/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml +++ b/Modules/ContentKit/Examples/ContentKitExample/Resources/activity.yml @@ -28,7 +28,7 @@ locales: hmi: - robot - magic_cards - - ipad_robot + - tablet_robot l10n: - locale: fr_FR diff --git a/Modules/ContentKit/Resources/Content/hmi/hmi.yml b/Modules/ContentKit/Resources/Content/hmi/hmi.yml index 816482afee..8a3c5bda5b 100644 --- a/Modules/ContentKit/Resources/Content/hmi/hmi.yml +++ b/Modules/ContentKit/Resources/Content/hmi/hmi.yml @@ -26,7 +26,7 @@ list: description: | The user interacts with the robot by using magic cards. - - id: ipad_robot + - id: tablet_robot l10n: - locale: fr_FR name: iPad et robot @@ -37,7 +37,7 @@ list: description: | The user interacts with the iPad, and the robot serves as support for observation before providing answers. - - id: ipad + - id: tablet l10n: - locale: fr_FR name: iPad diff --git a/Modules/ContentKit/Specs/jtd/activity.jtd.json b/Modules/ContentKit/Specs/jtd/activity.jtd.json index f1ad3022bf..c7c790bc09 100644 --- a/Modules/ContentKit/Specs/jtd/activity.jtd.json +++ b/Modules/ContentKit/Specs/jtd/activity.jtd.json @@ -64,7 +64,7 @@ ] }, "$hmi": { - "enum": ["robot", "ipad", "magic_cards", "ipad_robot"] + "enum": ["robot", "tablet", "magic_cards", "tablet_robot"] }, "$locale": { "enum": ["en_US", "fr_FR"] diff --git a/Modules/ContentKit/Specs/jtd/curriculum.jtd.json b/Modules/ContentKit/Specs/jtd/curriculum.jtd.json index 3aa3be7013..67f2532a9e 100644 --- a/Modules/ContentKit/Specs/jtd/curriculum.jtd.json +++ b/Modules/ContentKit/Specs/jtd/curriculum.jtd.json @@ -54,7 +54,7 @@ ] }, "$hmi": { - "enum": ["robot", "ipad", "magic_cards", "ipad_robot"] + "enum": ["robot", "tablet", "magic_cards", "tablet_robot"] }, "$status": { "enum": ["draft", "published"] diff --git a/Modules/ContentKit/Specs/tests/activity.yml b/Modules/ContentKit/Specs/tests/activity.yml index 1cfeeb6744..a1db126b5f 100644 --- a/Modules/ContentKit/Specs/tests/activity.yml +++ b/Modules/ContentKit/Specs/tests/activity.yml @@ -19,7 +19,7 @@ skills: hmi: - robot - magic_cards - - ipad_robot + - tablet_robot tags: - tag_one diff --git a/Modules/ContentKit/Specs/tests/curriculum.yml b/Modules/ContentKit/Specs/tests/curriculum.yml index 515fdd322d..3398f2c2cd 100644 --- a/Modules/ContentKit/Specs/tests/curriculum.yml +++ b/Modules/ContentKit/Specs/tests/curriculum.yml @@ -23,7 +23,7 @@ skills: hmi: - robot - magic_cards - - ipad_robot + - tablet_robot tags: - tag_one From 82a41afd6fd9a7f7f03382d55ca19bc805f0f129 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Feb 2024 18:00:30 +0100 Subject: [PATCH 8/8] :children_crossing: (ContentKit): HMI - improve name expressiveness --- Modules/ContentKit/Resources/Content/hmi/hmi.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/ContentKit/Resources/Content/hmi/hmi.yml b/Modules/ContentKit/Resources/Content/hmi/hmi.yml index 8a3c5bda5b..409c6c52fe 100644 --- a/Modules/ContentKit/Resources/Content/hmi/hmi.yml +++ b/Modules/ContentKit/Resources/Content/hmi/hmi.yml @@ -7,22 +7,22 @@ list: - id: robot l10n: - locale: fr_FR - name: Robot + name: Robot seul description: | L'utilisateur interagit directement avec le robot. - locale: en_US - name: Robot + name: Robot alone description: | The user interacts directly with the robot. - id: magic_cards l10n: - locale: fr_FR - name: Cartes magiques + name: Robot et cartes magiques description: | L'utilisateur interagit avec le robot en utilisant les cartes magiques. - locale: en_US - name: Magic cards + name: Robot and magic cards description: | The user interacts with the robot by using magic cards. @@ -40,10 +40,10 @@ list: - id: tablet l10n: - locale: fr_FR - name: iPad + name: iPad (robot en renforçateur) description: | L'utilisateur interagit avec l'iPad, le robot agit en tant que renforçateur. - locale: en_US - name: iPad + name: iPad (robot as reinforcer) description: | The user interacts with the iPad, and the robot serves as reinforcer.