From e32483fb2f80bb8d378e9fa9cace9e820d18d197 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Feb 2024 14:39:35 +0100 Subject: [PATCH 1/2] :bento: (ContentKit): Add activity type (one-on-one or group) --- ...70B59EB214A6AA11AB39D64D63990.activity.yml | 4 ++ ...2794F02D3423482E243BCBC7F8CA8.activity.yml | 4 ++ ...E9CA4B13B49AF96CD77A9DF90833B.activity.yml | 4 ++ ...6617499FE42ADB5B03B409462923E.activity.yml | 4 ++ ...853C4DC7B4D84A45AA20385F389F1.activity.yml | 4 ++ ...0B8BDD1B0497F87B32A390119DB98.activity.yml | 4 ++ ...3456789ABCDEF0123456789ABCDEF.activity.yml | 4 ++ .../Content/definitions/activity_types.yml | 38 +++++++++++++++++++ Specs/jtd/activity.jtd.json | 8 ++++ 9 files changed, 74 insertions(+) create mode 100644 Modules/ContentKit/Resources/Content/definitions/activity_types.yml diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_1-5A670B59EB214A6AA11AB39D64D63990.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_1-5A670B59EB214A6AA11AB39D64D63990.activity.yml index 4ec8b97d4d..2c1686d9fc 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_1-5A670B59EB214A6AA11AB39D64D63990.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_1-5A670B59EB214A6AA11AB39D64D63990.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_2-6102794F02D3423482E243BCBC7F8CA8.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_2-6102794F02D3423482E243BCBC7F8CA8.activity.yml index a86269fc35..d0760de1be 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_2-6102794F02D3423482E243BCBC7F8CA8.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_2-6102794F02D3423482E243BCBC7F8CA8.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_3-E7EE9CA4B13B49AF96CD77A9DF90833B.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_3-E7EE9CA4B13B49AF96CD77A9DF90833B.activity.yml index 5431592e26..595bb1ca09 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_3-E7EE9CA4B13B49AF96CD77A9DF90833B.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_3-E7EE9CA4B13B49AF96CD77A9DF90833B.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_4-81F6617499FE42ADB5B03B409462923E.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_4-81F6617499FE42ADB5B03B409462923E.activity.yml index 32a0edf80a..6298bfbcb4 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_4-81F6617499FE42ADB5B03B409462923E.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_4-81F6617499FE42ADB5B03B409462923E.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_5-725853C4DC7B4D84A45AA20385F389F1.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_5-725853C4DC7B4D84A45AA20385F389F1.activity.yml index bd9a5db66d..ca0e5cc80e 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_5-725853C4DC7B4D84A45AA20385F389F1.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_5-725853C4DC7B4D84A45AA20385F389F1.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/examples/sample_6-2FE0B8BDD1B0497F87B32A390119DB98.activity.yml b/Modules/ContentKit/Resources/Content/activities/examples/sample_6-2FE0B8BDD1B0497F87B32A390119DB98.activity.yml index 205401cb57..acbabadcb4 100644 --- a/Modules/ContentKit/Resources/Content/activities/examples/sample_6-2FE0B8BDD1B0497F87B32A390119DB98.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/examples/sample_6-2FE0B8BDD1B0497F87B32A390119DB98.activity.yml @@ -29,6 +29,10 @@ hmi: - magic_cards - tablet_robot +types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/activities/templates/activity_template-0123456789ABCDEF0123456789ABCDEF.activity.yml b/Modules/ContentKit/Resources/Content/activities/templates/activity_template-0123456789ABCDEF0123456789ABCDEF.activity.yml index 4dc7909456..99498d4d2b 100644 --- a/Modules/ContentKit/Resources/Content/activities/templates/activity_template-0123456789ABCDEF0123456789ABCDEF.activity.yml +++ b/Modules/ContentKit/Resources/Content/activities/templates/activity_template-0123456789ABCDEF0123456789ABCDEF.activity.yml @@ -23,6 +23,10 @@ tags: hmi: # {{💡TO_DEFINE}} - tablet +types: # {{💡TO_DEFINE}} + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Resources/Content/definitions/activity_types.yml b/Modules/ContentKit/Resources/Content/definitions/activity_types.yml new file mode 100644 index 0000000000..a54889438c --- /dev/null +++ b/Modules/ContentKit/Resources/Content/definitions/activity_types.yml @@ -0,0 +1,38 @@ +# Leka - iOS Monorepo +# Copyright APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +version: 1.0.0 +list: + - id: one_on_one + l10n: + - locale: fr_FR + name: ActivitĂ© en tĂȘte-Ă -tĂȘte + description: > + Une activitĂ© en tĂȘte-Ă -tĂȘte est un engagement personnalisĂ© entre un accompagnant et une seule personne accompagnĂ©e, + conçu pour favoriser une relation Ă©troite, une attention individuelle et des expĂ©riences d'apprentissage + ou de divertissement sur mesure. Ce type d'activitĂ© permet une interaction directe, un retour immĂ©diat + et des activitĂ©s spĂ©cifiquement adaptĂ©es aux intĂ©rĂȘts, capacitĂ©s et besoins de dĂ©veloppement de la personne accompagnĂ©e. + - locale: en_US + name: One-on-one activity + description: > + A one-on-one activity is a personalized engagement between one caregiver and one care receiver, designed to foster + a close relationship, individual attention, and tailored learning or entertainment experiences. This type of activity + allows for direct interaction, immediate feedback, and activities specifically tailored to the interests, abilities, + and developmental needs of the care receiver. + + - id: group + l10n: + - locale: fr_FR + name: ActivitĂ© en groupe + description: > + Une activitĂ© de groupe implique plusieurs personnes accommpagnĂ©es, supervisĂ©es par un accompagnant, s'engageant dans une tĂąche + ou un jeu partagĂ©. Elle est conçue pour encourager l'interaction sociale, le travail d'Ă©quipe et la rĂ©solution de problĂšmes collective. + Les activitĂ©s de groupe aident les personnes Ă  apprendre Ă  coopĂ©rer, partager et naviguer dans les dynamiques sociales tout en profitant + des avantages de perspectives diverses et d'un effort collaboratif. + - locale: en_US + name: Group activity + description: | + A group activity involves multiple care receivers, supervised by a caregiver, engaging in a shared task or game. It is designed to + encourage social interaction, teamwork, and collective problem-solving. Group activities help individuals learn to cooperate, share, and + navigate social dynamics while benefiting from diverse perspectives and collaborative effort. diff --git a/Specs/jtd/activity.jtd.json b/Specs/jtd/activity.jtd.json index a1a6ba96e5..cb98e437f9 100644 --- a/Specs/jtd/activity.jtd.json +++ b/Specs/jtd/activity.jtd.json @@ -27,6 +27,11 @@ "ref": "$hmi" } }, + "types": { + "elements": { + "ref": "$type" + } + }, "tags": { "elements": { "ref": "$tag" @@ -108,6 +113,9 @@ "$skill": { "type": "string" }, + "$type": { + "enum": ["one_on_one", "group"] + }, "$tag": { "type": "string" }, From 05dfa70d98182b4a2cc0f93cad6a9ade5778f714 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Feb 2024 15:04:02 +0100 Subject: [PATCH 2/2] :sparkles: (ContentKit): Add activity type (one-one-one, group) --- .../Activities/ActivityDetailsView.swift | 22 ++++ .../Sources/Activity/Activity.swift | 3 + .../Sources/Mocks/Activity+Mock.swift | 4 + .../Sources/Models/ActivityTypes.swift | 108 ++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 Modules/ContentKit/Sources/Models/ActivityTypes.swift diff --git a/Apps/LekaApp/Sources/_NEWCodeBase/Views/Activities/ActivityDetailsView.swift b/Apps/LekaApp/Sources/_NEWCodeBase/Views/Activities/ActivityDetailsView.swift index 08d4022ffe..dda6323e92 100644 --- a/Apps/LekaApp/Sources/_NEWCodeBase/Views/Activities/ActivityDetailsView.swift +++ b/Apps/LekaApp/Sources/_NEWCodeBase/Views/Activities/ActivityDetailsView.swift @@ -103,6 +103,27 @@ struct ActivityDetailsView: View { Text(hmi.description) } }) + + DisclosureGroup("**Activity Types**") { + ForEach(self.activity.types, id: \.self) { type in + let type = ActivityTypes.type(id: type)! + HStack { + Text(type.name) + Button { + self.selectedType = type + } label: { + Image(systemName: "info.circle") + } + } + } + } + .sheet(item: self.$selectedType, onDismiss: { self.selectedType = nil }, content: { type in + VStack(alignment: .leading) { + Text(type.name) + .font(.headline) + Text(type.description) + } + }) } Section { @@ -130,6 +151,7 @@ struct ActivityDetailsView: View { @State private var selectedAuthor: Author? @State private var selectedSkill: Skill? @State private var selectedHMI: HMIDetails? + @State private var selectedType: ActivityType? private let navigation: Navigation = .shared } diff --git a/Modules/ContentKit/Sources/Activity/Activity.swift b/Modules/ContentKit/Sources/Activity/Activity.swift index 74f30a7de8..229a30c1c9 100644 --- a/Modules/ContentKit/Sources/Activity/Activity.swift +++ b/Modules/ContentKit/Sources/Activity/Activity.swift @@ -24,6 +24,7 @@ public struct Activity: Decodable, Identifiable { 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.types = try container.decode([String].self, forKey: .types) self.tags = try container.decode([String].self, forKey: .tags) let localeStrings = try container.decode([String].self, forKey: .locales) @@ -42,6 +43,7 @@ public struct Activity: Decodable, 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 types: [String] // TODO: (@ladislas) - implement types public let tags: [String] // TODO: (@ladislas) - implement tags public let locales: [Locale] @@ -73,6 +75,7 @@ public struct Activity: Decodable, Identifiable { case authors case skills case hmi + case types case tags case status case locales diff --git a/Modules/ContentKit/Sources/Mocks/Activity+Mock.swift b/Modules/ContentKit/Sources/Mocks/Activity+Mock.swift index ae676be759..7075585d5d 100644 --- a/Modules/ContentKit/Sources/Mocks/Activity+Mock.swift +++ b/Modules/ContentKit/Sources/Mocks/Activity+Mock.swift @@ -40,6 +40,10 @@ public extension Activity { - magic_cards - tablet_robot + types: + - one_on_one + - group + locales: - en_US - fr_FR diff --git a/Modules/ContentKit/Sources/Models/ActivityTypes.swift b/Modules/ContentKit/Sources/Models/ActivityTypes.swift new file mode 100644 index 0000000000..745c732a3a --- /dev/null +++ b/Modules/ContentKit/Sources/Models/ActivityTypes.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: - ActivityTypes + +public class ActivityTypes: Codable { + // MARK: Lifecycle + + private init() { + self.container = Self.loadTypes() + } + + // MARK: Public + + public static var list: [ActivityType] { + shared.container.list + } + + public static func type(id: String) -> ActivityType? { + self.list.first(where: { $0.id == id }) + } + + // MARK: Private + + private struct TypesContainer: Codable { + let list: [ActivityType] + } + + private static let shared: ActivityTypes = .init() + + private let container: TypesContainer + + private static func loadTypes() -> TypesContainer { + if let fileURL = Bundle.module.url(forResource: "activity_types", withExtension: "yml") { + do { + let yamlString = try String(contentsOf: fileURL, encoding: .utf8) + let container = try YAMLDecoder().decode(TypesContainer.self, from: yamlString) + return container + } catch { + log.error("Failed to read YAML file: \(error)") + return TypesContainer(list: []) + } + } else { + log.error("activity_types.yml not found") + return TypesContainer(list: []) + } + } +} + +// MARK: - ActivityType + +public struct ActivityType: 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([ActivityType.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: ActivityType.Localization + +public extension ActivityType { + 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 + } +}