From fd63e443494dfa7148bccdc2b545510ce8dcfe62 Mon Sep 17 00:00:00 2001 From: woxtu Date: Tue, 3 Sep 2024 09:50:23 +0900 Subject: [PATCH 01/48] Update layout of timetable grid cells with multiple speakers --- app-ios/Sources/TimetableFeature/TimetableGridCard.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app-ios/Sources/TimetableFeature/TimetableGridCard.swift b/app-ios/Sources/TimetableFeature/TimetableGridCard.swift index 53a6e2bb1..d48d4350e 100644 --- a/app-ios/Sources/TimetableFeature/TimetableGridCard.swift +++ b/app-ios/Sources/TimetableFeature/TimetableGridCard.swift @@ -42,7 +42,14 @@ public struct TimetableGridCard: View { Spacer() - ForEach(timetableItem.speakers, id: \.id) { speaker in + if timetableItem.speakers.count > 1 { + HStack(spacing: 4) { + ForEach(timetableItem.speakers, id: \.id) { speaker in + CircularUserIcon(urlString: speaker.iconUrl) + .frame(width: 32, height: 32) + } + } + } else if let speaker = timetableItem.speakers.first { HStack(spacing: 8) { CircularUserIcon(urlString: speaker.iconUrl) .frame(width: 32, height: 32) From 67bc231f08473d966acd35dd2bb81a48bb365e1b Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 18:02:35 +0900 Subject: [PATCH 02/48] Remove unnecessary dependencies from TimetableFeature --- app-ios/Package.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 3c6d71b5a..1e5d2da2b 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -105,8 +105,6 @@ let package = Package( dependencies: [ .kmpClient, .kmpModule, - .firebaseAuth, - .firebaseRemoteConfig, .tca, .commonComponents, ] From 9e86400805da14949ffe2b40e8cc2ffb90da21c9 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 18:04:14 +0900 Subject: [PATCH 03/48] Remove unnecessary dependencies from TimetableTests --- app-ios/Package.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 1e5d2da2b..7a35e7734 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -114,8 +114,6 @@ let package = Package( dependencies: [ .app, .timetableFeature, - .firebaseAuth, - .firebaseRemoteConfig, .tca ] ), From fa17d2e47340ae79d9bf4c51852556b72baf67b8 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:00:12 +0900 Subject: [PATCH 04/48] remove unnecessary dependency --- app-ios/Package.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 3c6d71b5a..5fa8d2642 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -89,7 +89,6 @@ let package = Package( .firebaseRemoteConfig, .tca, .model, - .licenseList, ] ), From 874a4d401b28c2ab890b55a64b1531a23fb55654 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:03:23 +0900 Subject: [PATCH 05/48] Add KMPClientLive Target --- app-ios/Package.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 5fa8d2642..b23f10f3a 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -73,6 +73,7 @@ let package = Package( .eventMapFeature, .tca, .kmpClient, + .KMPClientLive, .licenseList, ] ), @@ -85,13 +86,20 @@ let package = Package( name: "KMPClient", dependencies: [ .kmpModule, - .firebaseAuth, - .firebaseRemoteConfig, .tca, .model, ] ), - + + .target( + name: "KMPClientLive", + dependencies: [ + .kmpClient, + .firebaseAuth, + .firebaseRemoteConfig, + ] + ), + .target( name: "EventKitClient", dependencies: [ @@ -292,6 +300,7 @@ extension Target.Dependency { static let profileCardFeature: Target.Dependency = "ProfileCardFeature" static let kmpModule: Target.Dependency = "KmpModule" static let kmpClient: Target.Dependency = "KMPClient" + static let KMPClientLive: Target.Dependency = "KMPClientLive" static let eventKitClient: Target.Dependency = "EventKitClient" static let theme: Target.Dependency = "Theme" static let commonComponents: Target.Dependency = "CommonComponents" From a0537ba23630a41b26fbdaf18bf2a6613b1c77ab Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:03:56 +0900 Subject: [PATCH 06/48] Use KMPClientLive Target on App --- app-ios/App/App.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/app-ios/App/App.swift b/app-ios/App/App.swift index e78707b84..e6ff6f41a 100644 --- a/app-ios/App/App.swift +++ b/app-ios/App/App.swift @@ -2,6 +2,7 @@ import App import ComposableArchitecture import SwiftUI import Theme +import KMPClientLive final class AppDelegate: NSObject, UIApplicationDelegate { let store = Store( From 6c58f47e0b5ff258bd1f58aead76b8cba14df4f2 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:20:17 +0900 Subject: [PATCH 07/48] Move LiveKey to KMPClientLive --- app-ios/Sources/{KMPClient => KMPClientLive}/LiveKey.swift | 2 ++ 1 file changed, 2 insertions(+) rename app-ios/Sources/{KMPClient => KMPClientLive}/LiveKey.swift (99%) diff --git a/app-ios/Sources/KMPClient/LiveKey.swift b/app-ios/Sources/KMPClientLive/LiveKey.swift similarity index 99% rename from app-ios/Sources/KMPClient/LiveKey.swift rename to app-ios/Sources/KMPClientLive/LiveKey.swift index 77f721d16..d0344a4a7 100644 --- a/app-ios/Sources/KMPClient/LiveKey.swift +++ b/app-ios/Sources/KMPClientLive/LiveKey.swift @@ -2,6 +2,8 @@ import Dependencies import shared import Model import Foundation +import Firebase +import KMPClient private var sessionsRepository: any SessionsRepository { Container.shared.get(type: (any SessionsRepository).self) From 0ed6703f6c6daba14d28a984db8fffb10ea91f5e Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:23:12 +0900 Subject: [PATCH 08/48] Move Container to KMPClientLive --- .../{KMPClient => KMPClientLive}/Helpers/Container.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename app-ios/Sources/{KMPClient => KMPClientLive}/Helpers/Container.swift (75%) diff --git a/app-ios/Sources/KMPClient/Helpers/Container.swift b/app-ios/Sources/KMPClientLive/Helpers/Container.swift similarity index 75% rename from app-ios/Sources/KMPClient/Helpers/Container.swift rename to app-ios/Sources/KMPClientLive/Helpers/Container.swift index e22939ec1..57b0be706 100644 --- a/app-ios/Sources/KMPClient/Helpers/Container.swift +++ b/app-ios/Sources/KMPClientLive/Helpers/Container.swift @@ -19,9 +19,9 @@ public struct Container: Sendable { } public func get(type: TypeProtocol) -> ReturnType where TypeProtocol: Protocol { - guard let object = entryPoint.get(objCProtocol: type) as? ReturnType else { - fatalError("Not found instance for \(type)") - } - return object + guard let object = entryPoint.get(objCProtocol: type) as? ReturnType else { + fatalError("Not found instance for \(type)") } + return object + } } From aa9607408c00e5e65b25c5ac69e445cd47663478 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:24:03 +0900 Subject: [PATCH 09/48] Move AuthenticatorImpl to KMPClientLive --- .../{KMPClient => KMPClientLive}/Helpers/Authenticator.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app-ios/Sources/{KMPClient => KMPClientLive}/Helpers/Authenticator.swift (100%) diff --git a/app-ios/Sources/KMPClient/Helpers/Authenticator.swift b/app-ios/Sources/KMPClientLive/Helpers/Authenticator.swift similarity index 100% rename from app-ios/Sources/KMPClient/Helpers/Authenticator.swift rename to app-ios/Sources/KMPClientLive/Helpers/Authenticator.swift From 12119d4a975b26591844d07f081adca52d6d9024 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:27:20 +0900 Subject: [PATCH 10/48] Move RemoteConfig to KMPClientLive --- .../{KMPClient => KMPClientLive}/Helpers/RemoteConfig.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app-ios/Sources/{KMPClient => KMPClientLive}/Helpers/RemoteConfig.swift (100%) diff --git a/app-ios/Sources/KMPClient/Helpers/RemoteConfig.swift b/app-ios/Sources/KMPClientLive/Helpers/RemoteConfig.swift similarity index 100% rename from app-ios/Sources/KMPClient/Helpers/RemoteConfig.swift rename to app-ios/Sources/KMPClientLive/Helpers/RemoteConfig.swift From 112662560640507b6e6fcabab52616175068e5e6 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:31:02 +0900 Subject: [PATCH 11/48] Add FirebaseAppClient --- app-ios/Sources/KMPClient/Client.swift | 10 ++++++++++ app-ios/Sources/KMPClientLive/Helpers/Container.swift | 4 ---- app-ios/Sources/KMPClientLive/LiveKey.swift | 10 ++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app-ios/Sources/KMPClient/Client.swift b/app-ios/Sources/KMPClient/Client.swift index 58aa594f3..493490f83 100644 --- a/app-ios/Sources/KMPClient/Client.swift +++ b/app-ios/Sources/KMPClient/Client.swift @@ -4,6 +4,11 @@ import shared import Model extension DependencyValues { + public var firebaseAppClient: FirebaseAppClient { + get { self[FirebaseAppClient.self] } + set { self[FirebaseAppClient.self] = newValue } + } + public var timetableClient: TimetableClient { get { self[TimetableClient.self] } set { self[TimetableClient.self] = newValue } @@ -35,6 +40,11 @@ extension DependencyValues { } } +@DependencyClient +public struct FirebaseAppClient: Sendable { + public var prepareFirebase: @Sendable () -> () +} + @DependencyClient public struct TimetableClient: Sendable { public var streamTimetable: @Sendable () throws -> AsyncThrowingStream diff --git a/app-ios/Sources/KMPClientLive/Helpers/Container.swift b/app-ios/Sources/KMPClientLive/Helpers/Container.swift index 57b0be706..f9da2f4df 100644 --- a/app-ios/Sources/KMPClientLive/Helpers/Container.swift +++ b/app-ios/Sources/KMPClientLive/Helpers/Container.swift @@ -2,10 +2,6 @@ import Firebase import ObjectiveC @preconcurrency import shared -public func prepareFirebase() { - FirebaseApp.configure() -} - public struct Container: Sendable { public static let shared: Container = .init() diff --git a/app-ios/Sources/KMPClientLive/LiveKey.swift b/app-ios/Sources/KMPClientLive/LiveKey.swift index d0344a4a7..88a357dcd 100644 --- a/app-ios/Sources/KMPClientLive/LiveKey.swift +++ b/app-ios/Sources/KMPClientLive/LiveKey.swift @@ -29,6 +29,16 @@ private var profileCardRepository: any ProfileCardRepository { Container.shared.get(type: (any ProfileCardRepository).self) } +extension FirebaseAppClient: DependencyKey { + public static var liveValue: Self { + Self( + prepareFirebase: { + FirebaseApp.configure() + } + ) + } +} + extension TimetableClient: DependencyKey { public static let liveValue: TimetableClient = .init( streamTimetable: { From ade5fdbe1e4ca7386bb7909bd950a1fab5bde8a7 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:32:22 +0900 Subject: [PATCH 12/48] Use FirebaseAppClient --- app-ios/Sources/App/AppDelegate.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app-ios/Sources/App/AppDelegate.swift b/app-ios/Sources/App/AppDelegate.swift index 7e53860d0..d203f330d 100644 --- a/app-ios/Sources/App/AppDelegate.swift +++ b/app-ios/Sources/App/AppDelegate.swift @@ -3,6 +3,7 @@ import KMPClient @Reducer public struct AppDelegateReducer { + @Dependency(\.firebaseAppClient) var firebaseAppClient public struct State: Equatable { public init() {} } @@ -15,7 +16,7 @@ public struct AppDelegateReducer { Reduce { state, action in switch action { case .didFinishLaunching: - prepareFirebase() + firebaseAppClient.prepareFirebase() return .none } From ae462b8a7312b82f34631662841979257301a96f Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:35:11 +0900 Subject: [PATCH 13/48] Add FirebaseAppClient TestDependencyKey --- app-ios/Sources/KMPClient/TestKey.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app-ios/Sources/KMPClient/TestKey.swift b/app-ios/Sources/KMPClient/TestKey.swift index 3526fe91f..eb92740fd 100644 --- a/app-ios/Sources/KMPClient/TestKey.swift +++ b/app-ios/Sources/KMPClient/TestKey.swift @@ -1,5 +1,10 @@ import Dependencies +extension FirebaseAppClient: TestDependencyKey { + public static let previewValue: Self = Self() + public static let testValue: Self = Self() +} + extension TimetableClient: TestDependencyKey { public static let previewValue: Self = Self() From 24808044a17e41edbea1e5a674cda92237deaabf Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:36:04 +0900 Subject: [PATCH 14/48] Add ContainerClient --- app-ios/Sources/KMPClient/Client.swift | 16 ++++++++++++++++ app-ios/Sources/KMPClient/TestKey.swift | 5 +++++ app-ios/Sources/KMPClientLive/LiveKey.swift | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/app-ios/Sources/KMPClient/Client.swift b/app-ios/Sources/KMPClient/Client.swift index 493490f83..03313a8d6 100644 --- a/app-ios/Sources/KMPClient/Client.swift +++ b/app-ios/Sources/KMPClient/Client.swift @@ -9,6 +9,11 @@ extension DependencyValues { set { self[FirebaseAppClient.self] = newValue } } + public var containerClient: ContainerClient { + get { self[ContainerClient.self] } + set { self[ContainerClient.self] = newValue } + } + public var timetableClient: TimetableClient { get { self[TimetableClient.self] } set { self[TimetableClient.self] = newValue } @@ -45,6 +50,17 @@ public struct FirebaseAppClient: Sendable { public var prepareFirebase: @Sendable () -> () } +@DependencyClient +public struct ContainerClient: Sendable { + private class DefaultRepositories: Repositories { + var map = [AnyHashable: Any]() + } + + public var repositories: @Sendable () -> any Repositories = { + DefaultRepositories() + } +} + @DependencyClient public struct TimetableClient: Sendable { public var streamTimetable: @Sendable () throws -> AsyncThrowingStream diff --git a/app-ios/Sources/KMPClient/TestKey.swift b/app-ios/Sources/KMPClient/TestKey.swift index eb92740fd..f1801bbca 100644 --- a/app-ios/Sources/KMPClient/TestKey.swift +++ b/app-ios/Sources/KMPClient/TestKey.swift @@ -5,6 +5,11 @@ extension FirebaseAppClient: TestDependencyKey { public static let testValue: Self = Self() } +extension ContainerClient: TestDependencyKey { + public static let previewValue: Self = Self() + public static let testValue: Self = Self() +} + extension TimetableClient: TestDependencyKey { public static let previewValue: Self = Self() diff --git a/app-ios/Sources/KMPClientLive/LiveKey.swift b/app-ios/Sources/KMPClientLive/LiveKey.swift index 88a357dcd..6d7949d63 100644 --- a/app-ios/Sources/KMPClientLive/LiveKey.swift +++ b/app-ios/Sources/KMPClientLive/LiveKey.swift @@ -39,6 +39,16 @@ extension FirebaseAppClient: DependencyKey { } } +extension ContainerClient: DependencyKey { + public static var liveValue: Self { + Self( + repositories: { + Container.shared.get(type: (any Repositories).self) + } + ) + } +} + extension TimetableClient: DependencyKey { public static let liveValue: TimetableClient = .init( streamTimetable: { From b62c960375917a5bd436b97f7203e1076452e7b0 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:36:54 +0900 Subject: [PATCH 15/48] Use ContainerClient --- .../ContributorFeature/KmpPresenterContributorView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift index 7e354a2c4..30ce8cdea 100644 --- a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift +++ b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift @@ -4,6 +4,7 @@ import Model import SwiftUI @preconcurrency import shared import Theme +import ComposableArchitecture struct KmpPresenterContributorView: View { private let repositories: any Repositories @@ -12,8 +13,9 @@ struct KmpPresenterContributorView: View { @State private var currentState: ContributorsUiState? = nil init(onContributorButtonTapped: @escaping (URL) -> Void) { - self.repositories = Container.shared.get(type: (any Repositories).self) + @Dependency(\.containerClient) var containerClient + self.repositories = containerClient.repositories() self.events = SkieKotlinSharedFlowFactory() .createSkieKotlinSharedFlow(replay: 0, extraBufferCapacity: 0) self.onContributorButtonTapped = onContributorButtonTapped From 9060d5875d8a95d72eb4d95f04eef21e3f816cfe Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:38:49 +0900 Subject: [PATCH 16/48] Move KmpAppComposeViewControllerWrapper to App/Views --- .../Views/KmpAppComposeViewControllerWrapper.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) rename app-ios/Sources/{KMPClient => App}/Views/KmpAppComposeViewControllerWrapper.swift (78%) diff --git a/app-ios/Sources/KMPClient/Views/KmpAppComposeViewControllerWrapper.swift b/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift similarity index 78% rename from app-ios/Sources/KMPClient/Views/KmpAppComposeViewControllerWrapper.swift rename to app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift index b5c64b8d7..5e592cc78 100644 --- a/app-ios/Sources/KMPClient/Views/KmpAppComposeViewControllerWrapper.swift +++ b/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift @@ -1,15 +1,17 @@ import SwiftUI import LicenseList -@preconcurrency import shared +import shared +import ComposableArchitecture +@MainActor public struct KmpAppComposeViewControllerWrapper: UIViewControllerRepresentable { + @Dependency(\.containerClient) var containerClient + public init() {} public func makeUIViewController(context: Context) -> UIViewController { - let container = Container.shared - let repositories: any Repositories = container.get(type: (any Repositories).self) - return IosComposeKaigiAppKt.kaigiAppController( - repositories: repositories, + IosComposeKaigiAppKt.kaigiAppController( + repositories: containerClient.repositories(), onLicenseScreenRequest: { openLicenseScreen() } @@ -18,7 +20,7 @@ public struct KmpAppComposeViewControllerWrapper: UIViewControllerRepresentable public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } - + private func openLicenseScreen() { if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { if let rootViewController = windowScene.windows.first?.rootViewController { From 4d44d92d18c504dd926ea0c2b203727e98cdeead Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:40:33 +0900 Subject: [PATCH 17/48] Change access level --- .../App/Views/KmpAppComposeViewControllerWrapper.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift b/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift index 5e592cc78..963ed1085 100644 --- a/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift +++ b/app-ios/Sources/App/Views/KmpAppComposeViewControllerWrapper.swift @@ -4,12 +4,12 @@ import shared import ComposableArchitecture @MainActor -public struct KmpAppComposeViewControllerWrapper: UIViewControllerRepresentable { +struct KmpAppComposeViewControllerWrapper: UIViewControllerRepresentable { @Dependency(\.containerClient) var containerClient - public init() {} + init() {} - public func makeUIViewController(context: Context) -> UIViewController { + func makeUIViewController(context: Context) -> UIViewController { IosComposeKaigiAppKt.kaigiAppController( repositories: containerClient.repositories(), onLicenseScreenRequest: { @@ -18,7 +18,7 @@ public struct KmpAppComposeViewControllerWrapper: UIViewControllerRepresentable ) } - public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } private func openLicenseScreen() { From 671b7080c761747ac54bd7d5fcce9d36ba043de1 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:45:32 +0900 Subject: [PATCH 18/48] Move KmpProfileCardComposeViewControllerWrapper to App/Views --- .../KmpProfileCardComposeViewControllerWrapper.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename app-ios/Sources/{KMPClient => App}/Views/KmpProfileCardComposeViewControllerWrapper.swift (85%) diff --git a/app-ios/Sources/KMPClient/Views/KmpProfileCardComposeViewControllerWrapper.swift b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift similarity index 85% rename from app-ios/Sources/KMPClient/Views/KmpProfileCardComposeViewControllerWrapper.swift rename to app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift index f77b2860f..1d840b359 100644 --- a/app-ios/Sources/KMPClient/Views/KmpProfileCardComposeViewControllerWrapper.swift +++ b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift @@ -3,15 +3,15 @@ import shared public struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepresentable { public init() {} - + public func makeUIViewController(context: Context) -> UIViewController { profileCardViewController( repositories: Container.shared.get(type: (any Repositories).self), onClickShareProfileCard: { image, text in let activityViewController = UIActivityViewController(activityItems: [text, image], applicationActivities: nil) if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }), - let rootViewController = keyWindow.rootViewController { + let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }), + let rootViewController = keyWindow.rootViewController { rootViewController.present(activityViewController, animated: true, completion: nil) } else { print("Unable to find the root view controller.") @@ -19,7 +19,7 @@ public struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepres } ) } - + public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } } From 66b47aa4c6e6dec9b99eeb2dfb58f464619cc215 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:46:33 +0900 Subject: [PATCH 19/48] Use ContainerClient --- .../Views/KmpProfileCardComposeViewControllerWrapper.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift index 1d840b359..583fd751c 100644 --- a/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift +++ b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift @@ -1,12 +1,15 @@ import SwiftUI import shared +import ComposableArchitecture public struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepresentable { + @Dependency(\.containerClient) var containerClient + public init() {} public func makeUIViewController(context: Context) -> UIViewController { profileCardViewController( - repositories: Container.shared.get(type: (any Repositories).self), + repositories: containerClient.repositories(), onClickShareProfileCard: { image, text in let activityViewController = UIActivityViewController(activityItems: [text, image], applicationActivities: nil) if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, From 8401fc821a30489a044839a8c3c46a747da69e16 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:47:14 +0900 Subject: [PATCH 20/48] Change access level --- .../KmpProfileCardComposeViewControllerWrapper.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift index 583fd751c..3e5676aaa 100644 --- a/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift +++ b/app-ios/Sources/App/Views/KmpProfileCardComposeViewControllerWrapper.swift @@ -2,12 +2,12 @@ import SwiftUI import shared import ComposableArchitecture -public struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepresentable { +struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepresentable { @Dependency(\.containerClient) var containerClient - public init() {} + init() {} - public func makeUIViewController(context: Context) -> UIViewController { + func makeUIViewController(context: Context) -> UIViewController { profileCardViewController( repositories: containerClient.repositories(), onClickShareProfileCard: { image, text in @@ -23,6 +23,6 @@ public struct KmpProfileCardComposeViewControllerWrapper: UIViewControllerRepres ) } - public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } } From 1568cd70899dc5ff21a9bf7ba21d5cd9f7cb8863 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:47:58 +0900 Subject: [PATCH 21/48] Move KmpContributorComposeViewControllerWrapper to ContributorFeature --- .../KmpContributorComposeViewControllerWrapper.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename app-ios/Sources/{KMPClient/Views => ContributorFeature}/KmpContributorComposeViewControllerWrapper.swift (83%) diff --git a/app-ios/Sources/KMPClient/Views/KmpContributorComposeViewControllerWrapper.swift b/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift similarity index 83% rename from app-ios/Sources/KMPClient/Views/KmpContributorComposeViewControllerWrapper.swift rename to app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift index 871d23637..41bdf9a4a 100644 --- a/app-ios/Sources/KMPClient/Views/KmpContributorComposeViewControllerWrapper.swift +++ b/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift @@ -1,5 +1,6 @@ import SwiftUI import shared +import ComposableArchitecture public struct KmpContributorComposeViewControllerWrapper: UIViewControllerRepresentable { public typealias URLString = String @@ -8,7 +9,9 @@ public struct KmpContributorComposeViewControllerWrapper: UIViewControllerRepres private let onContributorsItemClick: (URLString) -> Void public init(onContributorsItemClick: @escaping (URLString) -> Void) { - self.repositories = Container.shared.get(type: (any Repositories).self) + @Dependency(\.containerClient) var containerClient + + self.repositories = containerClient.repositories() self.onContributorsItemClick = onContributorsItemClick } From c03af9b4138260f3dacd1129a4ca24a7814ebc99 Mon Sep 17 00:00:00 2001 From: yimajo Date: Tue, 3 Sep 2024 22:49:41 +0900 Subject: [PATCH 22/48] Change access level --- .../KmpContributorComposeViewControllerWrapper.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift b/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift index 41bdf9a4a..3a55e417a 100644 --- a/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift +++ b/app-ios/Sources/ContributorFeature/KmpContributorComposeViewControllerWrapper.swift @@ -2,26 +2,26 @@ import SwiftUI import shared import ComposableArchitecture -public struct KmpContributorComposeViewControllerWrapper: UIViewControllerRepresentable { +struct KmpContributorComposeViewControllerWrapper: UIViewControllerRepresentable { public typealias URLString = String - public let repositories: any Repositories + let repositories: any Repositories private let onContributorsItemClick: (URLString) -> Void - public init(onContributorsItemClick: @escaping (URLString) -> Void) { + init(onContributorsItemClick: @escaping (URLString) -> Void) { @Dependency(\.containerClient) var containerClient self.repositories = containerClient.repositories() self.onContributorsItemClick = onContributorsItemClick } - public func makeUIViewController(context: Context) -> UIViewController { + func makeUIViewController(context: Context) -> UIViewController { return contributorsViewController( repositories: repositories, onContributorsItemClick: onContributorsItemClick ) } - public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } } From 5d8ace22bf7dfb29265153ced0de8782e8196955 Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Wed, 4 Sep 2024 03:11:05 +0900 Subject: [PATCH 23/48] :recycle: Added loading indicator since it is missing. --- .../contributors/ContributorsScreen.kt | 61 +++++++++++++------ .../ContributorsScreenPresenter.kt | 3 +- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt index c6902b369..16bd74215 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt @@ -1,11 +1,13 @@ package io.github.droidkaigi.confsched.contributors +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost @@ -13,6 +15,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.testTag @@ -46,10 +49,18 @@ fun NavGraphBuilder.contributorsScreens( } } -data class ContributorsUiState( - val contributors: PersistentList, - val userMessageStateHolder: UserMessageStateHolder, -) +sealed interface ContributorsUiState { + val userMessageStateHolder: UserMessageStateHolder + + data class Loading( + override val userMessageStateHolder: UserMessageStateHolder, + ): ContributorsUiState + + data class Exists( + override val userMessageStateHolder: UserMessageStateHolder, + val contributors: PersistentList, + ): ContributorsUiState +} @Composable fun ContributorsScreen( @@ -109,21 +120,33 @@ fun ContributorsScreen( } }, ) { padding -> - Contributors( - contributors = uiState.contributors, - onContributorsItemClick = onContributorsItemClick, - contentPadding = PaddingValues(bottom = padding.calculateBottomPadding()), - modifier = Modifier - .fillMaxSize() - .padding(top = padding.calculateTopPadding()) - .let { - if (scrollBehavior != null) { - it.nestedScroll(scrollBehavior.nestedScrollConnection) - } else { - it - } - }, - ) + when (uiState) { + is ContributorsUiState.Exists -> { + Contributors( + contributors = uiState.contributors, + onContributorsItemClick = onContributorsItemClick, + contentPadding = PaddingValues(bottom = padding.calculateBottomPadding()), + modifier = Modifier + .fillMaxSize() + .padding(top = padding.calculateTopPadding()) + .let { + if (scrollBehavior != null) { + it.nestedScroll(scrollBehavior.nestedScrollConnection) + } else { + it + } + }, + ) + } + is ContributorsUiState.Loading -> { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.padding(padding).fillMaxSize(), + ) { + CircularProgressIndicator() + } + } + } } } diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt index 93a73702b..4028126f4 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt @@ -19,7 +19,8 @@ fun contributorsScreenPresenter( val contributors by rememberUpdatedState(contributorsRepository.contributors()) EventEffect(events) { event -> } - ContributorsUiState( + if (contributors.isEmpty()) return@providePresenterDefaults ContributorsUiState.Loading(userMessageStateHolder) + ContributorsUiState.Exists( contributors = contributors, userMessageStateHolder = userMessageStateHolder, ) From 1a8be78bf22e55bf8e8b08c15851bb6378945fc7 Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Wed, 4 Sep 2024 03:22:22 +0900 Subject: [PATCH 24/48] :wrench: ./gradlew detekt --auto-correct --- .../droidkaigi/confsched/contributors/ContributorsScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt index 16bd74215..d90013170 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt @@ -54,12 +54,12 @@ sealed interface ContributorsUiState { data class Loading( override val userMessageStateHolder: UserMessageStateHolder, - ): ContributorsUiState + ) : ContributorsUiState data class Exists( override val userMessageStateHolder: UserMessageStateHolder, val contributors: PersistentList, - ): ContributorsUiState + ) : ContributorsUiState } @Composable From 924bf8efdf5e7f062e6926355cccb7f324554490 Mon Sep 17 00:00:00 2001 From: woxtu Date: Wed, 4 Sep 2024 04:45:03 +0900 Subject: [PATCH 25/48] Fix invalid ACLs --- .../ContributorFeature/SwiftUIContributorView.swift | 2 +- app-ios/Sources/TimetableFeature/TimetableListView.swift | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift b/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift index 7fde5d6bb..115183940 100644 --- a/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift +++ b/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift @@ -5,7 +5,7 @@ import Theme struct SwiftUIContributorView: View { private let store: StoreOf - public init(store: StoreOf) { + init(store: StoreOf) { self.store = store } diff --git a/app-ios/Sources/TimetableFeature/TimetableListView.swift b/app-ios/Sources/TimetableFeature/TimetableListView.swift index 4e8598832..fd56fefdc 100644 --- a/app-ios/Sources/TimetableFeature/TimetableListView.swift +++ b/app-ios/Sources/TimetableFeature/TimetableListView.swift @@ -84,7 +84,7 @@ public struct TimetableView: View { struct TimetableListView: View { private let store: StoreOf - public init(store: StoreOf) { + init(store: StoreOf) { self.store = store } @@ -110,7 +110,8 @@ struct TimetableListView: View { struct TimetableGridView: View { private let store: StoreOf - public init(store: StoreOf) { + + init(store: StoreOf) { self.store = store } @@ -204,7 +205,7 @@ struct TimeGroupMiniList: View { } struct DashedDivider: View { - public let axis: Axis + let axis: Axis var body: some View { let shape = LineShape(axis: axis) @@ -219,7 +220,7 @@ struct DashedDivider: View { } struct LineShape: Shape { - public let axis: Axis + let axis: Axis func path(in rect: CGRect) -> Path { var path = Path() From 734b8b3beca5aef257a5c3982c7571f65ea4cbdd Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Wed, 4 Sep 2024 06:03:58 +0900 Subject: [PATCH 26/48] :wrench: Fixed so that the build would also pass on the iOS side. Contributors Since it seems that Loading and Exists are not output to XCFrameWork when UiState is a sealed interface, I modified it like this. --- .../KmpPresenterContributorView.swift | 33 +++++++++++-------- .../contributors/ContributorsScreen.kt | 24 +++++++------- .../ContributorsScreenPresenter.kt | 4 +-- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift index 7e354a2c4..6ead06491 100644 --- a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift +++ b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift @@ -21,22 +21,26 @@ struct KmpPresenterContributorView: View { var body: some View { Group { - if let contributors = currentState.map(\.contributors) { - ScrollView { - LazyVStack(spacing: 0) { - ForEach(contributors, id: \.id) { value in - let contributor = Model.Contributor( - id: Int(value.id), - userName: value.username, - profileUrl: value.profileUrl.map { URL(string: $0)! } , - iconUrl: URL(string: value.iconUrl)! - ) - ContributorListItemView( - contributor: contributor, - onContributorButtonTapped: onContributorButtonTapped - ) + if let state = currentState { + if let existsState = state as? Exists { + ScrollView { + LazyVStack(spacing: 0) { + ForEach(existsState.contributors, id: \.id) { value in + let contributor = Model.Contributor( + id: Int(value.id), + userName: value.username, + profileUrl: value.profileUrl.map { URL(string: $0)! }, + iconUrl: URL(string: value.iconUrl)! + ) + ContributorListItemView( + contributor: contributor, + onContributorButtonTapped: onContributorButtonTapped + ) + } } } + } else if state is Loading { + ProgressView() } } else { ProgressView() @@ -51,6 +55,7 @@ struct KmpPresenterContributorView: View { @MainActor private func startListening() async { + // contributorsScreenPresenterStateFlowの非同期処理で状態を監視して更新 let uiStateStateFlow = contributorsScreenPresenterStateFlow(repositories: repositories.map, events: SkieSwiftMutableSharedFlow(events)) for await state in uiStateStateFlow { diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt index d90013170..dd923ecea 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreen.kt @@ -49,18 +49,18 @@ fun NavGraphBuilder.contributorsScreens( } } -sealed interface ContributorsUiState { - val userMessageStateHolder: UserMessageStateHolder +sealed class ContributorsUiState { + abstract val userMessageStateHolder: UserMessageStateHolder +} - data class Loading( - override val userMessageStateHolder: UserMessageStateHolder, - ) : ContributorsUiState +class Loading( + override val userMessageStateHolder: UserMessageStateHolder, +) : ContributorsUiState() - data class Exists( - override val userMessageStateHolder: UserMessageStateHolder, - val contributors: PersistentList, - ) : ContributorsUiState -} +class Exists( + override val userMessageStateHolder: UserMessageStateHolder, + val contributors: PersistentList, +) : ContributorsUiState() @Composable fun ContributorsScreen( @@ -121,7 +121,7 @@ fun ContributorsScreen( }, ) { padding -> when (uiState) { - is ContributorsUiState.Exists -> { + is Exists -> { Contributors( contributors = uiState.contributors, onContributorsItemClick = onContributorsItemClick, @@ -138,7 +138,7 @@ fun ContributorsScreen( }, ) } - is ContributorsUiState.Loading -> { + is Loading -> { Box( contentAlignment = Alignment.Center, modifier = Modifier.padding(padding).fillMaxSize(), diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt index 4028126f4..53ae2aad0 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenPresenter.kt @@ -19,8 +19,8 @@ fun contributorsScreenPresenter( val contributors by rememberUpdatedState(contributorsRepository.contributors()) EventEffect(events) { event -> } - if (contributors.isEmpty()) return@providePresenterDefaults ContributorsUiState.Loading(userMessageStateHolder) - ContributorsUiState.Exists( + if (contributors.isEmpty()) return@providePresenterDefaults Loading(userMessageStateHolder) + Exists( contributors = contributors, userMessageStateHolder = userMessageStateHolder, ) From 4234e4d89df7922e67c649516297e2af0d710d7b Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Wed, 4 Sep 2024 07:11:46 +0900 Subject: [PATCH 27/48] :wastebasket: Remove unnecessary comment. --- .../Sources/ContributorFeature/KmpPresenterContributorView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift index 6ead06491..c9cf9f494 100644 --- a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift +++ b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift @@ -55,7 +55,6 @@ struct KmpPresenterContributorView: View { @MainActor private func startListening() async { - // contributorsScreenPresenterStateFlowの非同期処理で状態を監視して更新 let uiStateStateFlow = contributorsScreenPresenterStateFlow(repositories: repositories.map, events: SkieSwiftMutableSharedFlow(events)) for await state in uiStateStateFlow { From 50e7cdbeb58c0fb4109426884d6789c79dbf057c Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Mon, 2 Sep 2024 18:45:28 +0900 Subject: [PATCH 28/48] Introduce AutoSizeText component & use it for the top bar title of all main screens --- .../designsystem/component/AutoSizeText.kt | 94 +++++++++++++++++++ .../component/AnimatedTextTopAppBar.kt | 8 +- .../confsched/sessions/TimetableScreen.kt | 14 ++- 3 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt new file mode 100644 index 000000000..f64d6132b --- /dev/null +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -0,0 +1,94 @@ +package io.github.droidkaigi.confsched.designsystem.component + +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.BoxWithConstraintsScope +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalFontFamilyResolver +import androidx.compose.ui.text.Paragraph +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.TextUnit +import kotlin.math.ceil + +/** + * A [Text] component that automatically downsizes its font size to fit its content, + * with the upper bound being defined by the font size of its [style]. + */ +@Composable +fun AutoSizeText( + text: String, + modifier: Modifier = Modifier, + textAlign: TextAlign = TextAlign.Unspecified, + maxLines: Int = Int.MAX_VALUE, + style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, +) { + BoxWithConstraints( + modifier = modifier, + contentAlignment = when (textAlign) { + TextAlign.Left, TextAlign.Start -> Alignment.CenterStart + TextAlign.Center -> Alignment.Center + TextAlign.Right, TextAlign.End -> Alignment.CenterEnd + TextAlign.Justify -> Alignment.Center + else -> Alignment.CenterStart + }, + ) { + Text( + text = text, + color = color, + fontSize = calculateFontSize(text, style, color, textAlign, maxLines), + style = style, + maxLines = maxLines, + textAlign = textAlign, + ) + } +} + +@Composable +private fun BoxWithConstraintsScope.calculateFontSize( + text: String, + style: TextStyle, + color: Color, + textAlign: TextAlign, + maxLines: Int, +): TextUnit = with(LocalDensity.current) { + // Upper bound of the font size, will decrease in the `while` loop below + var targetFontSize = style.fontSize + + // Calculate the text layout using the current `targetFontSize` + val calculateParagraph = @Composable { + val finalStyle = style.merge( + TextStyle( + color = color, + fontSize = targetFontSize, + textAlign = textAlign, + ), + ) + + Paragraph( + text = text, + style = finalStyle, + maxLines = maxLines, + constraints = Constraints(maxWidth = ceil(maxWidth.toPx()).toInt()), + density = this, + fontFamilyResolver = LocalFontFamilyResolver.current, + ) + } + + var paragraph = calculateParagraph() + + // Keep decreasing the font size until the text fits in the box without exceeding the max lines + while (paragraph.didExceedMaxLines || maxHeight < paragraph.height.toDp() || maxWidth < paragraph.minIntrinsicWidth.toDp()) { + targetFontSize *= 0.95 + paragraph = calculateParagraph() + } + + targetFontSize +} diff --git a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedTextTopAppBar.kt b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedTextTopAppBar.kt index 6075e6e1b..1c781edf4 100644 --- a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedTextTopAppBar.kt +++ b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedTextTopAppBar.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults @@ -17,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.style.TextAlign +import io.github.droidkaigi.confsched.designsystem.component.AutoSizeText @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -38,7 +38,7 @@ fun AnimatedTextTopAppBar( TopAppBar( title = { Box(modifier = Modifier.fillMaxWidth()) { - Text( + AutoSizeText( text = title, color = textColor, modifier = Modifier @@ -47,10 +47,11 @@ fun AnimatedTextTopAppBar( alpha = 1f - transitionFraction }, textAlign = TextAlign.Start, + maxLines = 1, style = MaterialTheme.typography.headlineSmall, ) - Text( + AutoSizeText( text = title, color = textColor, modifier = Modifier @@ -60,6 +61,7 @@ fun AnimatedTextTopAppBar( alpha = transitionFraction }, textAlign = TextAlign.Center, + maxLines = 1, style = MaterialTheme.typography.titleMedium, ) } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt index 49c349b89..410fdb1a1 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt @@ -18,10 +18,10 @@ import androidx.compose.material.icons.filled.Search import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -31,9 +31,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation.NavController import androidx.navigation.NavGraph.Companion.findStartDestination @@ -47,6 +45,7 @@ import conference_app_2024.feature.sessions.generated.resources.timeline_view import conference_app_2024.feature.sessions.generated.resources.timetable import io.github.droidkaigi.confsched.compose.EventFlow import io.github.droidkaigi.confsched.compose.rememberEventFlow +import io.github.droidkaigi.confsched.designsystem.component.AutoSizeText import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched.droidkaigiui.SnackbarMessageEffect import io.github.droidkaigi.confsched.droidkaigiui.UserMessageStateHolder @@ -158,12 +157,11 @@ private fun TimetableScreen( Row( verticalAlignment = Alignment.CenterVertically, ) { - Text( + AutoSizeText( + modifier = Modifier.weight(1f), text = stringResource(SessionsRes.string.timetable), - fontSize = 24.sp, - lineHeight = 32.sp, - fontWeight = FontWeight.W400, - modifier = Modifier.weight(1F), + style = MaterialTheme.typography.headlineSmall, + maxLines = 1, ) IconButton( onClick = dropUnlessResumed(block = onSearchClick), From 0e3f70f2f4b289ca75cc0b4fa2bfd3b8056f2eb6 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Sat, 31 Aug 2024 08:37:27 +0900 Subject: [PATCH 29/48] Apply AutoSizeText to other title bars in the app --- .../droidkaigiui/component/AnimatedLargeTopAppBar.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedLargeTopAppBar.kt b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedLargeTopAppBar.kt index fd5271c20..55db9a3d1 100644 --- a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedLargeTopAppBar.kt +++ b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedLargeTopAppBar.kt @@ -14,7 +14,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior @@ -29,6 +28,7 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched.designsystem.component.AutoSizeText @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -68,7 +68,7 @@ fun AnimatedLargeTopAppBar( // No animation required as it is erased with alpha exit = ExitTransition.None, ) { - Text( + AutoSizeText( text = title, modifier = Modifier.then( when (isCenterTitle) { @@ -82,6 +82,7 @@ fun AnimatedLargeTopAppBar( }, ), textAlign = TextAlign.Center, + maxLines = 1, ) } }, From 1d3ecb6419fabc74763405795869e0546e7acddb Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Mon, 2 Sep 2024 21:11:25 +0900 Subject: [PATCH 30/48] Apply AutoTextSize to the new AnimatedMediumTopAppBar --- .../droidkaigiui/component/AnimatedMediumTopAppBar.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedMediumTopAppBar.kt b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedMediumTopAppBar.kt index 86635b829..6c6080814 100644 --- a/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedMediumTopAppBar.kt +++ b/core/droidkaigiui/src/commonMain/kotlin/io/github/droidkaigi/confsched/droidkaigiui/component/AnimatedMediumTopAppBar.kt @@ -14,7 +14,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MediumTopAppBar -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior @@ -29,6 +28,7 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched.designsystem.component.AutoSizeText @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -68,7 +68,7 @@ fun AnimatedMediumTopAppBar( // No animation required as it is erased with alpha exit = ExitTransition.None, ) { - Text( + AutoSizeText( text = title, modifier = Modifier.then( when (isCenterTitle) { @@ -77,11 +77,13 @@ fun AnimatedMediumTopAppBar( .padding(end = navigationIconWidthDp.dp) .fillMaxWidth() } + false -> Modifier null -> Modifier.alpha(0f) }, ), textAlign = TextAlign.Center, + maxLines = 1, ) } }, From a01e906e40e4f5f0219e009984e24a90df622f7d Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Tue, 3 Sep 2024 19:36:11 +0900 Subject: [PATCH 31/48] Find the best font size for AutoTextSize using binary search, if needed --- .../designsystem/component/AutoSizeText.kt | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt index f64d6132b..0cbca1919 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -15,6 +15,9 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp import kotlin.math.ceil /** @@ -58,37 +61,60 @@ private fun BoxWithConstraintsScope.calculateFontSize( color: Color, textAlign: TextAlign, maxLines: Int, -): TextUnit = with(LocalDensity.current) { - // Upper bound of the font size, will decrease in the `while` loop below - var targetFontSize = style.fontSize - - // Calculate the text layout using the current `targetFontSize` - val calculateParagraph = @Composable { +): TextUnit { + // Helper function to calculate if a given text size + // would cause an overflow when placed into this BoxWithConstraints + val hasOverflowWhenPlaced: @Composable TextUnit.() -> Boolean = { val finalStyle = style.merge( TextStyle( color = color, - fontSize = targetFontSize, + fontSize = this, textAlign = textAlign, ), ) - Paragraph( - text = text, - style = finalStyle, - maxLines = maxLines, - constraints = Constraints(maxWidth = ceil(maxWidth.toPx()).toInt()), - density = this, - fontFamilyResolver = LocalFontFamilyResolver.current, - ) + with(LocalDensity.current) { + Paragraph( + text = text, + style = finalStyle, + maxLines = maxLines, + constraints = Constraints(maxWidth = ceil(maxWidth.toPx()).toInt()), + density = this, + fontFamilyResolver = LocalFontFamilyResolver.current, + ).run { + didExceedMaxLines || maxHeight < height.toDp() || maxWidth < minIntrinsicWidth.toDp() + } + } } - var paragraph = calculateParagraph() + // If the original text size fits already without overflowing, + // then there is no need to do anything + if (!style.fontSize.hasOverflowWhenPlaced()) { + return style.fontSize + } + + // Otherwise, find the biggest font size that still fits using binary search + var lo = 1 + var hi = style.fontSize.value.toInt() + val type = style.fontSize.type - // Keep decreasing the font size until the text fits in the box without exceeding the max lines - while (paragraph.didExceedMaxLines || maxHeight < paragraph.height.toDp() || maxWidth < paragraph.minIntrinsicWidth.toDp()) { - targetFontSize *= 0.95 - paragraph = calculateParagraph() + while (lo <= hi) { + val mid = lo + (hi - lo) / 2 + if (mid.asTextUnit(type).hasOverflowWhenPlaced()) { + hi = mid - 1 + } else { + lo = mid + 1 + } } - targetFontSize + // After the binary search, the right pointer is the largest size + // that still works without overflowing the box' + return hi.asTextUnit(type) +} + +private fun Int.asTextUnit(type: TextUnitType) = when (type) { + TextUnitType.Sp -> this.sp + TextUnitType.Em -> this.em + TextUnitType.Unspecified -> TextUnit.Unspecified + else -> error("Invalid TextUnitType: $type") } From 9c82a2e7787d1ce9d22d92662627740a1798e22e Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Tue, 3 Sep 2024 20:50:24 +0900 Subject: [PATCH 32/48] Add font scale test for AutoSizeText to timetable screen This required a new type of matcher for asserting the line count of a text node --- .../designsystem/component/AutoSizeText.kt | 3 ++- .../testing/robot/TimetableScreenRobot.kt | 12 +++++++++++- .../confsched/testing/utils/Matchers.kt | 19 +++++++++++++++++++ .../confsched/sessions/TimetableScreenTest.kt | 12 ++++++++++++ .../confsched/sessions/TimetableScreen.kt | 3 ++- 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt index 0cbca1919..063046646 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalFontFamilyResolver +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.Paragraph import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign @@ -34,7 +35,7 @@ fun AutoSizeText( color: Color = Color.Unspecified, ) { BoxWithConstraints( - modifier = modifier, + modifier = modifier.semantics(mergeDescendants = true) {}, contentAlignment = when (textAlign) { TextAlign.Left, TextAlign.Start -> Alignment.CenterStart TextAlign.Center -> Alignment.Center diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/TimetableScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/TimetableScreenRobot.kt index 80da65f33..ac886e908 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/TimetableScreenRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/TimetableScreenRobot.kt @@ -27,11 +27,13 @@ import io.github.droidkaigi.confsched.model.DroidKaigi2024Day import io.github.droidkaigi.confsched.model.TimetableItem import io.github.droidkaigi.confsched.sessions.TimetableScreen import io.github.droidkaigi.confsched.sessions.TimetableScreenTestTag +import io.github.droidkaigi.confsched.sessions.TimetableTitleTestTag import io.github.droidkaigi.confsched.sessions.TimetableUiTypeChangeButtonTestTag import io.github.droidkaigi.confsched.sessions.component.TimetableGridItemTestTag import io.github.droidkaigi.confsched.sessions.section.TimetableGridTestTag import io.github.droidkaigi.confsched.sessions.section.TimetableListTestTag import io.github.droidkaigi.confsched.sessions.section.TimetableTabTestTag +import io.github.droidkaigi.confsched.testing.utils.assertLineCount import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant @@ -41,9 +43,11 @@ class TimetableScreenRobot @Inject constructor( private val screenRobot: DefaultScreenRobot, private val timetableServerRobot: DefaultTimetableServerRobot, private val deviceSetupRobot: DefaultDeviceSetupRobot, + fontScaleRobot: DefaultFontScaleRobot, ) : ScreenRobot by screenRobot, TimetableServerRobot by timetableServerRobot, - DeviceSetupRobot by deviceSetupRobot { + DeviceSetupRobot by deviceSetupRobot, + FontScaleRobot by fontScaleRobot { val clickedItems = mutableSetOf() fun setupTimetableScreenContent(customTime: LocalDateTime? = null) { @@ -68,6 +72,12 @@ class TimetableScreenRobot @Inject constructor( waitUntilIdle() } + fun checkTitleDisplayedInSingleLine() { + composeTestRule + .onNode(hasTestTag(TimetableTitleTestTag)) + .assertLineCount(1) + } + fun clickFirstSession() { composeTestRule .onAllNodes(hasTestTag(TimetableItemCardTestTag)) diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/utils/Matchers.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/utils/Matchers.kt index 5d612613c..3ad7ceb7a 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/utils/Matchers.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/utils/Matchers.kt @@ -1,12 +1,15 @@ package io.github.droidkaigi.confsched.testing.utils +import androidx.compose.ui.semantics.SemanticsActions import androidx.compose.ui.semantics.SemanticsNode import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.semantics.getOrNull import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.SemanticsNodeInteractionCollection +import androidx.compose.ui.text.TextLayoutResult import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue fun hasTestTag( testTag: String, @@ -66,6 +69,22 @@ fun SemanticsNodeInteraction.assertTextDoesNotContain( } } +fun SemanticsNodeInteraction.assertLineCount(expectedCount: Int) { + fetchSemanticsNode() + .let { node -> + val results = mutableListOf() + node.config.getOrNull(SemanticsActions.GetTextLayoutResult) + ?.action + ?.invoke(results) + val result = results.firstOrNull() + + assertTrue( + "Node has unexpected line count (expected $expectedCount, but was ${result?.lineCount})", + result?.lineCount == expectedCount, + ) + } +} + private fun buildErrorMessageForMinimumCountMismatch( errorMessage: String, foundNodes: List, diff --git a/feature/sessions/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreenTest.kt b/feature/sessions/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreenTest.kt index 1beee51f1..bcc315cec 100644 --- a/feature/sessions/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreenTest.kt +++ b/feature/sessions/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreenTest.kt @@ -128,6 +128,18 @@ class TimetableScreenTest(private val testCase: DescribedBehavior Unit, @@ -158,7 +159,7 @@ private fun TimetableScreen( verticalAlignment = Alignment.CenterVertically, ) { AutoSizeText( - modifier = Modifier.weight(1f), + modifier = Modifier.testTag(TimetableTitleTestTag).weight(1f), text = stringResource(SessionsRes.string.timetable), style = MaterialTheme.typography.headlineSmall, maxLines = 1, From 9b7456b959624566d29badfbca05e40766938c74 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Tue, 3 Sep 2024 20:53:35 +0900 Subject: [PATCH 33/48] Small comment fix --- .../droidkaigi/confsched/designsystem/component/AutoSizeText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt index 063046646..80933d7e9 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -109,7 +109,7 @@ private fun BoxWithConstraintsScope.calculateFontSize( } // After the binary search, the right pointer is the largest size - // that still works without overflowing the box' + // that still works without overflowing the box return hi.asTextUnit(type) } From be532bb4a0380db939a5b00aadb65762b4faa19b Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Wed, 4 Sep 2024 19:24:16 +0900 Subject: [PATCH 34/48] Remember the calculated font size to avoid unnecessary recalculation --- .../designsystem/component/AutoSizeText.kt | 90 ++++++++++--------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt index 80933d7e9..ad25fea6d 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.BoxWithConstraintsScope import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -47,7 +48,7 @@ fun AutoSizeText( Text( text = text, color = color, - fontSize = calculateFontSize(text, style, color, textAlign, maxLines), + fontSize = rememberFontSize(text, style, color, textAlign, maxLines), style = style, maxLines = maxLines, textAlign = textAlign, @@ -56,61 +57,66 @@ fun AutoSizeText( } @Composable -private fun BoxWithConstraintsScope.calculateFontSize( +private fun BoxWithConstraintsScope.rememberFontSize( text: String, style: TextStyle, color: Color, textAlign: TextAlign, maxLines: Int, ): TextUnit { - // Helper function to calculate if a given text size - // would cause an overflow when placed into this BoxWithConstraints - val hasOverflowWhenPlaced: @Composable TextUnit.() -> Boolean = { - val finalStyle = style.merge( - TextStyle( - color = color, - fontSize = this, - textAlign = textAlign, - ), - ) + val density = LocalDensity.current + val fontFamilyResolver = LocalFontFamilyResolver.current + + return remember(text, style.fontSize, textAlign, maxLines, density, maxWidth, maxHeight) { + // Helper function to calculate if a given text size + // would cause an overflow when placed into this BoxWithConstraints + val hasOverflowWhenPlaced: TextUnit.() -> Boolean = { + val finalStyle = style.merge( + TextStyle( + color = color, + fontSize = this, + textAlign = textAlign, + ), + ) - with(LocalDensity.current) { - Paragraph( - text = text, - style = finalStyle, - maxLines = maxLines, - constraints = Constraints(maxWidth = ceil(maxWidth.toPx()).toInt()), - density = this, - fontFamilyResolver = LocalFontFamilyResolver.current, - ).run { - didExceedMaxLines || maxHeight < height.toDp() || maxWidth < minIntrinsicWidth.toDp() + with(density) { + Paragraph( + text = text, + style = finalStyle, + maxLines = maxLines, + constraints = Constraints(maxWidth = ceil(maxWidth.toPx()).toInt()), + density = this, + fontFamilyResolver = fontFamilyResolver, + ).run { + didExceedMaxLines || maxHeight < height.toDp() || maxWidth < minIntrinsicWidth.toDp() + } } } - } - // If the original text size fits already without overflowing, - // then there is no need to do anything - if (!style.fontSize.hasOverflowWhenPlaced()) { - return style.fontSize - } + // If the original text size fits already without overflowing, + // then there is no need to do anything + if (!style.fontSize.hasOverflowWhenPlaced()) { + style.fontSize + } - // Otherwise, find the biggest font size that still fits using binary search - var lo = 1 - var hi = style.fontSize.value.toInt() - val type = style.fontSize.type + // Otherwise, find the biggest font size that still fits using binary search + var lo = 1 + var hi = style.fontSize.value.toInt() + val type = style.fontSize.type - while (lo <= hi) { - val mid = lo + (hi - lo) / 2 - if (mid.asTextUnit(type).hasOverflowWhenPlaced()) { - hi = mid - 1 - } else { - lo = mid + 1 + while (lo <= hi) { + val mid = lo + (hi - lo) / 2 + if (mid.asTextUnit(type).hasOverflowWhenPlaced()) { + hi = mid - 1 + } else { + lo = mid + 1 + } } - } - // After the binary search, the right pointer is the largest size - // that still works without overflowing the box - return hi.asTextUnit(type) + // After the binary search, the right pointer is the largest size + // that still works without overflowing the box + hi.asTextUnit(type) + } } private fun Int.asTextUnit(type: TextUnitType) = when (type) { From d0ad0ffdaf88c1620bdfd664ae7f860d794392b4 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Thu, 5 Sep 2024 00:50:37 +0200 Subject: [PATCH 35/48] Update core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt Co-authored-by: Takahiro Menju --- .../droidkaigi/confsched/designsystem/component/AutoSizeText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt index ad25fea6d..20d773e33 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/component/AutoSizeText.kt @@ -96,7 +96,7 @@ private fun BoxWithConstraintsScope.rememberFontSize( // If the original text size fits already without overflowing, // then there is no need to do anything if (!style.fontSize.hasOverflowWhenPlaced()) { - style.fontSize + return@remember style.fontSize } // Otherwise, find the biggest font size that still fits using binary search From 9c8842e8a9bb7bd43062796be1dca3687ecec961 Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:14:59 +0900 Subject: [PATCH 36/48] :wrench: As before, I forgot to fix the arguments in IosComposeKaigiTest.kt, so I fixed them again. https://github.com/DroidKaigi/conference-app-2024/pull/881 --- .../droidkaigi/confsched/shared/IosComposeKaigiTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiTest.kt b/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiTest.kt index 53c04e7ed..4af1d995c 100644 --- a/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiTest.kt +++ b/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiTest.kt @@ -1,8 +1,10 @@ package io.github.droidkaigi.confsched.shared +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowSizeClass.Companion.calculateFromSize import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember import androidx.compose.ui.geometry.Size import androidx.compose.ui.platform.LocalDensity import androidx.lifecycle.compose.LocalLifecycleOwner @@ -17,6 +19,7 @@ import io.github.droidkaigi.confsched.data.Repositories import io.github.droidkaigi.confsched.data.auth.Authenticator import io.github.droidkaigi.confsched.data.auth.User import io.github.droidkaigi.confsched.data.remoteconfig.FakeRemoteConfigApi +import io.github.droidkaigi.confsched.droidkaigiui.compositionlocal.LocalSnackbarHostState import io.github.droidkaigi.confsched.model.compositionlocal.LocalRepositories import io.github.takahirom.roborazzi.captureRoboImage import kotlin.test.Test @@ -45,12 +48,16 @@ class IosComposeKaigiTest { ) runComposeUiTest { setContent { + val snackbarHostState = remember { SnackbarHostState() } + CompositionLocalProvider( LocalLifecycleOwner provides object : LifecycleOwner { override val lifecycle: Lifecycle = LifecycleRegistry(this) }, LocalRepositories provides kmpEntryPoint.get().map, + LocalSnackbarHostState provides snackbarHostState ) { + KaigiApp( windowSize = calculateFromSize( size = Size( @@ -60,6 +67,8 @@ class IosComposeKaigiTest { density = LocalDensity.current, ), fontFamily = null, + snackbarHostState = snackbarHostState, + onLicenseScreenRequest = {}, ) } } From 76656e1c39fc91d82157a42e62fc8842584457ac Mon Sep 17 00:00:00 2001 From: norihirosunada Date: Thu, 5 Sep 2024 10:12:26 +0900 Subject: [PATCH 37/48] Fixed the color of the icon in empty favorite sheet --- .../droidkaigi/confsched/favorites/section/FavoriteSheet.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/feature/favorites/src/commonMain/kotlin/io/github/droidkaigi/confsched/favorites/section/FavoriteSheet.kt b/feature/favorites/src/commonMain/kotlin/io/github/droidkaigi/confsched/favorites/section/FavoriteSheet.kt index d7e302de4..0334def1a 100644 --- a/feature/favorites/src/commonMain/kotlin/io/github/droidkaigi/confsched/favorites/section/FavoriteSheet.kt +++ b/feature/favorites/src/commonMain/kotlin/io/github/droidkaigi/confsched/favorites/section/FavoriteSheet.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.unit.dp import conference_app_2024.feature.favorites.generated.resources.empty_description import conference_app_2024.feature.favorites.generated.resources.empty_guide import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.primaryFixed import io.github.droidkaigi.confsched.favorites.FavoritesRes import io.github.droidkaigi.confsched.favorites.component.FavoriteFilters import io.github.droidkaigi.confsched.favorites.section.FavoritesSheetUiState.FavoriteListUiState.TimeSlot @@ -154,7 +155,7 @@ private fun EmptyView(modifier: Modifier = Modifier) { modifier = Modifier.size(36.dp), imageVector = Icons.Filled.Favorite, contentDescription = null, - tint = Color.Green, + tint = MaterialTheme.colorScheme.primaryFixed, ) } Spacer(Modifier.height(12.dp)) From b30906921163b9dbc8e510159cc2fc0270be1ec2 Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:26:33 +0900 Subject: [PATCH 38/48] =?UTF-8?q?:memo:=20The=20method=20with=20the=20pref?= =?UTF-8?q?ix=20=E2=80=9CPreview=E2=80=9D=20has=20been=20renamed=20so=20th?= =?UTF-8?q?at=20the=20suffix=20has=20=E2=80=9CPreview=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../droidkaigi/confsched/eventmap/EventMapScreen.kt | 2 +- .../droidkaigi/confsched/sessions/TimetableScreen.kt | 2 +- .../confsched/sessions/component/TimetableGridItem.kt | 10 +++++----- .../confsched/sessions/component/TimetableGridTab.kt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/EventMapScreen.kt b/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/EventMapScreen.kt index 06143988e..cc6298b74 100644 --- a/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/EventMapScreen.kt +++ b/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/EventMapScreen.kt @@ -207,7 +207,7 @@ private fun EventMap( @Composable @Preview -fun PreviewEventMapScreen() { +fun EventMapScreenPreview() { EventMapScreen( uiState = EventMapUiState.Exists( userMessageStateHolder = rememberUserMessageStateHolder(), diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt index f14a21ea9..d73319f34 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableScreen.kt @@ -225,7 +225,7 @@ private fun TimetableScreen( @Preview @Composable -fun PreviewTimetableScreenDark() { +fun TimetableScreenDarkPreview() { CompositionLocalProvider(LocalClock provides FakeClock) { KaigiTheme { TimetableScreen( diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt index 17fb3bb4e..a79837759 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt @@ -494,7 +494,7 @@ object TimetableGridItemSizes { @Preview @Composable -fun PreviewTimetableGridItem() { +fun TimetableGridItemPreview() { KaigiTheme { Surface { TimetableGridItem( @@ -509,7 +509,7 @@ fun PreviewTimetableGridItem() { @Preview @Composable -fun PreviewTimetableGridLongTitleItem() { +fun TimetableGridLongTitleItemPreview() { val fake = Session.fake() val localDensity = LocalDensity.current @@ -538,7 +538,7 @@ fun PreviewTimetableGridLongTitleItem() { @Preview @Composable -fun PreviewTimetableGridMultiSpeakersItem() { +fun TimetableGridMultiSpeakersItemPreview() { KaigiTheme { Surface { TimetableGridItem( @@ -552,7 +552,7 @@ fun PreviewTimetableGridMultiSpeakersItem() { @Preview @Composable -fun PreviewTimetableGridItemWelcomeTalk() { +fun TimetableGridItemWelcomeTalkPreview() { KaigiTheme { Surface { TimetableGridItem( @@ -597,7 +597,7 @@ fun PreviewTimetableGridItemWelcomeTalk() { @Preview @Composable -fun PreviewTimetableGridItemMoreLongTitleItem() { +fun TimetableGridItemMoreLongTitleItemPreview() { KaigiTheme { Surface { TimetableGridItem( diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridTab.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridTab.kt index af606c27d..6afe33cc3 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridTab.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridTab.kt @@ -123,7 +123,7 @@ private fun FloorText( @Preview @Composable -fun PreviewTimetableDayTab() { +fun TimetableDayTabPreview() { KaigiTheme { Surface { TimetableDayTab( From 0d5d891ab8b298da638ce47c146c0f39d24e73ad Mon Sep 17 00:00:00 2001 From: todayama_r <13657682+Corvus400@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:49:16 +0900 Subject: [PATCH 39/48] =?UTF-8?q?:memo:=20The=20method=20with=20the=20pref?= =?UTF-8?q?ix=20=E2=80=9CPreview=E2=80=9D=20has=20been=20renamed=20so=20th?= =?UTF-8?q?at=20the=20suffix=20has=20=E2=80=9CPreview=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/github/droidkaigi/confsched/sessions/SearchScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/SearchScreen.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/SearchScreen.kt index a623a8eb9..205b7573d 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/SearchScreen.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/SearchScreen.kt @@ -189,7 +189,7 @@ fun SearchScreen( @Preview @Composable -fun SearchScreenPreview_Empty() { +fun EmptySearchScreenPreview() { KaigiTheme { SearchScreen( uiState = SearchScreenUiState.Empty( From 79f7fe058cb39424f7c2a7b373f242bed16dcef2 Mon Sep 17 00:00:00 2001 From: Aniokrait Date: Thu, 5 Sep 2024 15:41:21 +0900 Subject: [PATCH 40/48] Fix KotlinGradleDsl --- .../primitive/AndroidKotlinPlugin.kt | 36 ++++++++++++------- .../confsched/primitive/KmpKtorfitPlugin.kt | 3 -- .../confsched/primitive/KotlinGradleDsl.kt | 9 ++--- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt index 95b888568..4c9e5c238 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt @@ -1,9 +1,10 @@ package io.github.droidkaigi.confsched.primitive -import org.gradle.api.JavaVersion import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 @Suppress("unused") class AndroidKotlinPlugin : Plugin { @@ -13,22 +14,28 @@ class AndroidKotlinPlugin : Plugin { apply("org.jetbrains.kotlin.android") } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java) { - kotlinOptions.jvmTarget = "11" + compilerOptions { + jvmTarget.set(JVM_11) + } } android { - kotlinOptions { - // Treat all Kotlin warnings as errors (disabled by default) - allWarningsAsErrors = properties["warningsAsErrors"] as? Boolean ?: false + kotlinAndroidOptions { + kotlinAndroid { + compilerOptions { + // Treat all Kotlin warnings as errors (disabled by default) + allWarningsAsErrors.set(properties["warningsAsErrors"] as? Boolean ?: false) - freeCompilerArgs = freeCompilerArgs + listOf( -// "-opt-in=kotlin.RequiresOptIn", - // Enable experimental coroutines APIs, including Flow -// "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-Xcontext-receivers" - ) + freeCompilerArgs.addAll(listOf( +// "-opt-in=kotlin.RequiresOptIn", + // Enable experimental coroutines APIs, including Flow +// "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-Xcontext-receivers" + )) - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget.set(JVM_11) + } + } } } dependencies { @@ -38,4 +45,9 @@ class AndroidKotlinPlugin : Plugin { } } } + + private fun Project.kotlinAndroid(configure: org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension.() -> Unit) { + extensions.configure(configure) + } + } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpKtorfitPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpKtorfitPlugin.kt index 1155da6f6..d5c60fed9 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpKtorfitPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpKtorfitPlugin.kt @@ -1,6 +1,5 @@ package io.github.droidkaigi.confsched.primitive -import com.google.devtools.ksp.gradle.KspTaskNative import io.github.droidkaigi.confsched.primitive.Arch.ALL import io.github.droidkaigi.confsched.primitive.Arch.ARM import io.github.droidkaigi.confsched.primitive.Arch.ARM_SIMULATOR_DEBUG @@ -10,8 +9,6 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.withType -import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink @Suppress("unused") class KmpKtorfitPlugin : Plugin { diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt index 71301c6fc..398754d10 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt @@ -1,13 +1,14 @@ package io.github.droidkaigi.confsched.primitive -import com.android.build.gradle.TestedExtension +import org.gradle.api.Project import org.gradle.api.artifacts.MinimalExternalModuleDependency import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.DependencyHandlerScope -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.gradle.kotlin.dsl.configure +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -fun TestedExtension.kotlinOptions(block: KotlinJvmOptions.() -> Unit) { - (this as ExtensionAware).extensions.configure("kotlinOptions", block) +fun Project.kotlinAndroidOptions(block: KotlinAndroidProjectExtension.() -> Unit) { + (this as ExtensionAware).extensions.configure(block) } fun DependencyHandlerScope.ksp( From 07e9530555b63ec5c0453d97c9fe4d77bf46f6b6 Mon Sep 17 00:00:00 2001 From: Aniokrait Date: Thu, 5 Sep 2024 17:18:20 +0900 Subject: [PATCH 41/48] Fix usage of deprecated gradle plugins --- .../convention/AndroidFeaturePlugin.kt | 12 ++++----- .../primitive/AndroidKotlinPlugin.kt | 26 +++++++------------ .../confsched/primitive/KmpAndroidPlugin.kt | 9 +++++-- .../confsched/primitive/KmpComposePlugin.kt | 4 +-- .../confsched/primitive/KmpIosPlugin.kt | 14 +++++++--- .../confsched/primitive/KmpPlugin.kt | 5 +++- .../confsched/primitive/KotlinGradleDsl.kt | 10 ++++--- 7 files changed, 46 insertions(+), 34 deletions(-) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/convention/AndroidFeaturePlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/convention/AndroidFeaturePlugin.kt index 70b2bd6eb..0fa67fc4b 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/convention/AndroidFeaturePlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/convention/AndroidFeaturePlugin.kt @@ -20,8 +20,8 @@ class AndroidFeaturePlugin : Plugin { } tasks.withType().configureEach { - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters() + compilerOptions { + freeCompilerArgs.addAll(buildComposeMetricsParameters()) } } } @@ -35,20 +35,20 @@ fun Project.buildComposeMetricsParameters(): List { val enableMetrics = (enableMetricsProvider.orNull == "true") if (enableMetrics) { - val metricsFolder = rootProject.buildDir.resolve("compose-metrics").resolve(relativePath) + val metricsFolder = rootProject.layout.buildDirectory.orNull?.asFile?.resolve("compose-metrics")?.resolve(relativePath) metricParameters.add("-P") metricParameters.add( - "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder?.absolutePath ) } val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports") val enableReports = (enableReportsProvider.orNull == "true") if (enableReports) { - val reportsFolder = rootProject.buildDir.resolve("compose-reports").resolve(relativePath) + val reportsFolder = rootProject.layout.buildDirectory.orNull?.asFile?.resolve("compose-reports")?.resolve(relativePath) metricParameters.add("-P") metricParameters.add( - "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder?.absolutePath ) } return metricParameters.toList() diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt index 4c9e5c238..4b52349fe 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt @@ -2,7 +2,6 @@ package io.github.droidkaigi.confsched.primitive import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 @@ -20,21 +19,19 @@ class AndroidKotlinPlugin : Plugin { } android { - kotlinAndroidOptions { - kotlinAndroid { - compilerOptions { - // Treat all Kotlin warnings as errors (disabled by default) - allWarningsAsErrors.set(properties["warningsAsErrors"] as? Boolean ?: false) + kotlinAndroid { + compilerOptions { + // Treat all Kotlin warnings as errors (disabled by default) + allWarningsAsErrors.set(properties["warningsAsErrors"] as? Boolean ?: false) - freeCompilerArgs.addAll(listOf( + freeCompilerArgs.addAll(listOf( // "-opt-in=kotlin.RequiresOptIn", - // Enable experimental coroutines APIs, including Flow + // Enable experimental coroutines APIs, including Flow // "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-Xcontext-receivers" - )) + "-Xcontext-receivers" + )) - jvmTarget.set(JVM_11) - } + jvmTarget.set(JVM_11) } } } @@ -45,9 +42,4 @@ class AndroidKotlinPlugin : Plugin { } } } - - private fun Project.kotlinAndroid(configure: org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension.() -> Unit) { - extensions.configure(configure) - } - } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt index 2cc84515e..b8614eb94 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt @@ -4,6 +4,7 @@ import com.google.devtools.ksp.gradle.KspTaskMetadata import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 @Suppress("unused") class KmpAndroidPlugin : Plugin { @@ -15,8 +16,12 @@ class KmpAndroidPlugin : Plugin { kotlin { androidTarget { compilations.all { - kotlinOptions { - jvmTarget = "11" + libraryAndroid { + compileTaskProvider.configure { + compilerOptions { + jvmTarget.set(JVM_11) + } + } } } } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpComposePlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpComposePlugin.kt index cbb3290fc..f1b620aa7 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpComposePlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpComposePlugin.kt @@ -54,8 +54,8 @@ class KmpComposePlugin : Plugin { } tasks.withType().configureEach { - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters() + compilerOptions { + freeCompilerArgs.addAll(buildComposeMetricsParameters()) } } } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpIosPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpIosPlugin.kt index b0b7097e3..f34e938d7 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpIosPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpIosPlugin.kt @@ -8,10 +8,12 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget @Suppress("unused") class KmpIosPlugin : Plugin { + @OptIn(ExperimentalKotlinGradlePluginApi::class) override fun apply(target: Project) { with(target) { with(pluginManager) { @@ -56,9 +58,15 @@ class KmpIosPlugin : Plugin { applyDefaultHierarchyTemplate() targets.withType { - // export kdoc to header file - // https://kotlinlang.org/docs/native-objc-interop.html#export-of-kdoc-comments-to-generated-objective-c-headers - compilations["main"].kotlinOptions.freeCompilerArgs += "-Xexport-kdoc" + compilations["main"].apply { + kotlin { + compilerOptions { + // export kdoc to header file + // https://kotlinlang.org/docs/native-objc-interop.html#export-of-kdoc-comments-to-generated-objective-c-headers + freeCompilerArgs.add("-Xexport-kdoc") + } + } + } } } } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpPlugin.kt index 7d21fc900..b61468cba 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpPlugin.kt @@ -5,10 +5,13 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @Suppress("unused") class KmpPlugin : Plugin { + @OptIn(ExperimentalKotlinGradlePluginApi::class) override fun apply(target: Project) { with(target) { with(pluginManager) { @@ -21,7 +24,7 @@ class KmpPlugin : Plugin { } } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java) { - kotlinOptions.jvmTarget = "11" + compilerOptions.jvmTarget.set(JVM_11) } tasks.withType().configureEach { notCompatibleWithConfigurationCache("Configuration chache not supported for a system property read at configuration time") diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt index 398754d10..68fa3afba 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt @@ -1,14 +1,18 @@ package io.github.droidkaigi.confsched.primitive +import com.android.build.api.variant.LibraryAndroidComponentsExtension import org.gradle.api.Project import org.gradle.api.artifacts.MinimalExternalModuleDependency -import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.configure import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -fun Project.kotlinAndroidOptions(block: KotlinAndroidProjectExtension.() -> Unit) { - (this as ExtensionAware).extensions.configure(block) +fun Project.kotlinAndroid(configure: KotlinAndroidProjectExtension.() -> Unit) { + extensions.configure(configure) +} + +fun Project.libraryAndroid(configure: LibraryAndroidComponentsExtension.() -> Unit) { + extensions.configure(configure) } fun DependencyHandlerScope.ksp( From 82dc5ddfd2c535161c29e0c088bf5a66587c34bc Mon Sep 17 00:00:00 2001 From: Aniokrait Date: Thu, 5 Sep 2024 17:19:00 +0900 Subject: [PATCH 42/48] Rename DSL --- .../droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt | 2 +- .../github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt | 2 +- .../github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt index 4b52349fe..096f86824 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidKotlinPlugin.kt @@ -19,7 +19,7 @@ class AndroidKotlinPlugin : Plugin { } android { - kotlinAndroid { + kotlinAndroidOptions { compilerOptions { // Treat all Kotlin warnings as errors (disabled by default) allWarningsAsErrors.set(properties["warningsAsErrors"] as? Boolean ?: false) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt index b8614eb94..6a4e0d2e0 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidPlugin.kt @@ -16,7 +16,7 @@ class KmpAndroidPlugin : Plugin { kotlin { androidTarget { compilations.all { - libraryAndroid { + libraryAndroidOptions { compileTaskProvider.configure { compilerOptions { jvmTarget.set(JVM_11) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt index 68fa3afba..1bca14c26 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt @@ -7,11 +7,11 @@ import org.gradle.kotlin.dsl.DependencyHandlerScope import org.gradle.kotlin.dsl.configure import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -fun Project.kotlinAndroid(configure: KotlinAndroidProjectExtension.() -> Unit) { +fun Project.kotlinAndroidOptions(configure: KotlinAndroidProjectExtension.() -> Unit) { extensions.configure(configure) } -fun Project.libraryAndroid(configure: LibraryAndroidComponentsExtension.() -> Unit) { +fun Project.libraryAndroidOptions(configure: LibraryAndroidComponentsExtension.() -> Unit) { extensions.configure(configure) } From efac5bed3ec883df3814daceeb6868b1630ff767 Mon Sep 17 00:00:00 2001 From: Aniokrait Date: Thu, 5 Sep 2024 17:35:45 +0900 Subject: [PATCH 43/48] Move definition --- .../confsched/primitive/AndroidGradleDsl.kt | 10 ++++++++++ .../confsched/primitive/KotlinGradleDsl.kt | 12 ------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidGradleDsl.kt index 5988bf727..540621e2f 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidGradleDsl.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidGradleDsl.kt @@ -1,6 +1,7 @@ package io.github.droidkaigi.confsched.primitive import com.android.build.api.dsl.CommonExtension +import com.android.build.api.variant.LibraryAndroidComponentsExtension import com.android.build.gradle.LibraryExtension import com.android.build.gradle.TestedExtension import com.android.build.gradle.internal.dsl.BaseAppModuleExtension @@ -13,6 +14,7 @@ import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension fun Project.androidApplication(action: BaseAppModuleExtension.() -> Unit) { extensions.configure(action) @@ -26,6 +28,14 @@ fun Project.android(action: TestedExtension.() -> Unit) { extensions.configure(action) } +fun Project.kotlinAndroidOptions(configure: KotlinAndroidProjectExtension.() -> Unit) { + extensions.configure(configure) +} + +fun Project.libraryAndroidOptions(configure: LibraryAndroidComponentsExtension.() -> Unit) { + extensions.configure(configure) +} + fun Project.setupAndroid() { android { namespace?.let { diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt index 1bca14c26..322a36623 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KotlinGradleDsl.kt @@ -1,19 +1,7 @@ package io.github.droidkaigi.confsched.primitive -import com.android.build.api.variant.LibraryAndroidComponentsExtension -import org.gradle.api.Project import org.gradle.api.artifacts.MinimalExternalModuleDependency import org.gradle.kotlin.dsl.DependencyHandlerScope -import org.gradle.kotlin.dsl.configure -import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension - -fun Project.kotlinAndroidOptions(configure: KotlinAndroidProjectExtension.() -> Unit) { - extensions.configure(configure) -} - -fun Project.libraryAndroidOptions(configure: LibraryAndroidComponentsExtension.() -> Unit) { - extensions.configure(configure) -} fun DependencyHandlerScope.ksp( artifact: MinimalExternalModuleDependency, From 15529f07810a246dc78b258a8627fb0a09b04e2d Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Thu, 5 Sep 2024 18:43:51 +0900 Subject: [PATCH 44/48] Move v35-only theme declaration into the correct themes.xml --- app-android/src/main/res/values-v35/themes.xml | 6 ++++++ app-android/src/main/res/values/themes.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 app-android/src/main/res/values-v35/themes.xml diff --git a/app-android/src/main/res/values-v35/themes.xml b/app-android/src/main/res/values-v35/themes.xml new file mode 100644 index 000000000..794c4f3d7 --- /dev/null +++ b/app-android/src/main/res/values-v35/themes.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app-android/src/main/res/values/themes.xml b/app-android/src/main/res/values/themes.xml index 28918f934..83052c864 100644 --- a/app-android/src/main/res/values/themes.xml +++ b/app-android/src/main/res/values/themes.xml @@ -6,6 +6,6 @@ From 40dfa403d09f6f610afd482e5cd8dd91a28c6311 Mon Sep 17 00:00:00 2001 From: yimajo Date: Thu, 5 Sep 2024 19:12:57 +0900 Subject: [PATCH 45/48] Improve ContributorFeatureTests Modify to correctly assert error reception --- .../ContributorFeatureTests/ContributorFeatureTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-ios/Tests/ContributorFeatureTests/ContributorFeatureTests.swift b/app-ios/Tests/ContributorFeatureTests/ContributorFeatureTests.swift index b497cf8fc..b33fdf163 100644 --- a/app-ios/Tests/ContributorFeatureTests/ContributorFeatureTests.swift +++ b/app-ios/Tests/ContributorFeatureTests/ContributorFeatureTests.swift @@ -23,7 +23,7 @@ final class ContributorFeatureTests: XCTestCase { } await store.send(.onAppear) - await store.receive(\.response) { + await store.receive(\.response.success) { $0.contributors = [ .init(id: 0, userName: "hoge", profileUrl: URL(string: "https://2024.droidkaigi.jp/"), iconUrl: URL(string: "https://avatars.githubusercontent.com/u/10727543?s=200&v=4")!), .init(id: 0, userName: "fuga", profileUrl: nil, iconUrl: URL(string: "https://avatars.githubusercontent.com/u/10727543?s=200&v=4")!), @@ -47,7 +47,7 @@ final class ContributorFeatureTests: XCTestCase { } await store.send(.onAppear) - await store.receive(\.response) + await store.receive(\.response.failure) } @MainActor From 60db87aaa762ac6f7cc3050b47f07f5fd9537f81 Mon Sep 17 00:00:00 2001 From: yimajo Date: Thu, 5 Sep 2024 19:13:26 +0900 Subject: [PATCH 46/48] Improve TimetableDetailTests Modify to correctly assert error reception --- .../TimetableDetailTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app-ios/Tests/TimetableDetailFeatureTests/TimetableDetailTests.swift b/app-ios/Tests/TimetableDetailFeatureTests/TimetableDetailTests.swift index fe1019396..30b235dd6 100644 --- a/app-ios/Tests/TimetableDetailFeatureTests/TimetableDetailTests.swift +++ b/app-ios/Tests/TimetableDetailFeatureTests/TimetableDetailTests.swift @@ -15,14 +15,14 @@ final class TimetableDetail_iosTests: XCTestCase { // add favorite await store.send(.view(.favoriteButtonTapped)) - await store.receive(\.favoriteResponse) { + await store.receive(\.favoriteResponse.success) { $0.isFavorited = !isFavorited $0.toast = .init(text: String(localized: "TimetableDetailAddBookmark", bundle: .module)) } // remove favorite await store.send(.view(.favoriteButtonTapped)) - await store.receive(\.favoriteResponse) { + await store.receive(\.favoriteResponse.success) { $0.isFavorited = isFavorited } @@ -36,7 +36,7 @@ final class TimetableDetail_iosTests: XCTestCase { } await store.send(.view(.favoriteButtonTapped)) - await store.receive(\.favoriteResponse) + await store.receive(\.favoriteResponse.failure) } @MainActor func testTappedURL() async throws { @@ -58,7 +58,7 @@ final class TimetableDetail_iosTests: XCTestCase { } await store.send(.view(.calendarButtonTapped)) - await store.receive(\.requestEventAccessResponse) { + await store.receive(\.requestEventAccessResponse.success) { $0.confirmationDialog = ConfirmationDialogState(title: { TextState("") }, actions: { @@ -71,7 +71,7 @@ final class TimetableDetail_iosTests: XCTestCase { await store.send(.confirmationDialog(.presented(.addEvent))) { $0.confirmationDialog = nil } - await store.receive(\.addEventResponse) + await store.receive(\.addEventResponse.success) } @MainActor func testTappedVideoButton() async throws { From 3b21a36d18caf3d24d23c036efc13c9ae720f5bd Mon Sep 17 00:00:00 2001 From: yimajo Date: Thu, 5 Sep 2024 19:54:36 +0900 Subject: [PATCH 47/48] Rename test method --- app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift b/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift index e1dad8ee8..f22b5344a 100644 --- a/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift +++ b/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift @@ -5,7 +5,7 @@ import ComposableArchitecture final class SponsorFeatureTests: XCTestCase { @MainActor - func testExample() async throws { + func testOnAppear() async throws { let store = TestStore(initialState: SponsorReducer.State()) { SponsorReducer() } withDependencies: { From 02a170f24efc626ce49687b582ce3c83994171c5 Mon Sep 17 00:00:00 2001 From: yimajo Date: Thu, 5 Sep 2024 20:11:08 +0900 Subject: [PATCH 48/48] Rename test method --- app-ios/Tests/StaffFeatureTests/StaffFeatureTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-ios/Tests/StaffFeatureTests/StaffFeatureTests.swift b/app-ios/Tests/StaffFeatureTests/StaffFeatureTests.swift index 5058f46ec..a04b34198 100644 --- a/app-ios/Tests/StaffFeatureTests/StaffFeatureTests.swift +++ b/app-ios/Tests/StaffFeatureTests/StaffFeatureTests.swift @@ -5,7 +5,7 @@ import ComposableArchitecture final class StaffFeatureTests: XCTestCase { @MainActor - func testExample() async throws { + func testOnAppear() async throws { let store = TestStore(initialState: StaffReducer.State()) { StaffReducer() } withDependencies: {