From 05dfa70d98182b4a2cc0f93cad6a9ade5778f714 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Feb 2024 15:04:02 +0100 Subject: [PATCH] :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 + } +}