From e7f462bdc4ad3b222f0ebb3e845b788bd9a47c47 Mon Sep 17 00:00:00 2001 From: "Md. Ahsan Ullah Rasel" Date: Fri, 6 Sep 2024 11:55:31 +0900 Subject: [PATCH 1/6] Add necessary string resources for contributor total counts --- .../src/commonMain/composeResources/values-ja/strings.xml | 2 ++ .../src/commonMain/composeResources/values/strings.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/feature/contributors/src/commonMain/composeResources/values-ja/strings.xml b/feature/contributors/src/commonMain/composeResources/values-ja/strings.xml index dd7db785c..691ed3d49 100644 --- a/feature/contributors/src/commonMain/composeResources/values-ja/strings.xml +++ b/feature/contributors/src/commonMain/composeResources/values-ja/strings.xml @@ -1,4 +1,6 @@ コントリビューター + 合計 + diff --git a/feature/contributors/src/commonMain/composeResources/values/strings.xml b/feature/contributors/src/commonMain/composeResources/values/strings.xml index 5ac45c4ff..71bd9eb6a 100644 --- a/feature/contributors/src/commonMain/composeResources/values/strings.xml +++ b/feature/contributors/src/commonMain/composeResources/values/strings.xml @@ -1,4 +1,6 @@ Contributor + Total + persons From 67128a4ada6012970d760bbac215a2ed9c3ae6a0 Mon Sep 17 00:00:00 2001 From: "Md. Ahsan Ullah Rasel" Date: Fri, 6 Sep 2024 12:07:06 +0900 Subject: [PATCH 2/6] Implement contributors total count in Android and iOS Compose Screen --- .../contributors/ContributorsScreen.kt | 10 +++ .../component/ContributorsCountItem.kt | 63 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt 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..c18cfc2ce 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 @@ -21,6 +21,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import conference_app_2024.feature.contributors.generated.resources.contributor_title import io.github.droidkaigi.confsched.compose.rememberEventFlow +import io.github.droidkaigi.confsched.contributors.component.ContributorsCountItem import io.github.droidkaigi.confsched.contributors.component.ContributorsItem import io.github.droidkaigi.confsched.droidkaigiui.SnackbarMessageEffect import io.github.droidkaigi.confsched.droidkaigiui.UserMessageStateHolder @@ -33,6 +34,7 @@ const val contributorsScreenRoute = "contributors" const val ContributorsScreenTestTag = "ContributorsScreenTestTag" const val ContributorsTestTag = "ContributorsTestTag" const val ContributorsItemTestTagPrefix = "ContributorsItemTestTag:" +const val ContributorsTotalCountTestTag = "ContributorsTotalCountTestTag" fun NavGraphBuilder.contributorsScreens( onNavigationIconClick: () -> Unit, @@ -138,6 +140,14 @@ private fun Contributors( modifier = modifier.testTag(ContributorsTestTag), contentPadding = contentPadding, ) { + item { + ContributorsCountItem( + totalContributor = contributors.size, + modifier = Modifier + .fillMaxWidth() + .testTag(ContributorsTotalCountTestTag), + ) + } items(contributors) { ContributorsItem( contributor = it, diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt new file mode 100644 index 000000000..1d4130119 --- /dev/null +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt @@ -0,0 +1,63 @@ +package io.github.droidkaigi.confsched.contributors.component + +import androidx.compose.animation.core.EaseOutQuart +import androidx.compose.animation.core.animateIntAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import conference_app_2024.feature.contributors.generated.resources.contributor_person +import conference_app_2024.feature.contributors.generated.resources.contributor_title +import conference_app_2024.feature.contributors.generated.resources.contributor_total +import io.github.droidkaigi.confsched.contributors.ContributorsRes +import org.jetbrains.compose.resources.stringResource + +@Composable +internal fun ContributorsCountItem( + totalContributor: Int, + modifier: Modifier = Modifier, +) { + val animatedTotalContributor by animateIntAsState( + targetValue = totalContributor, + animationSpec = tween( + delayMillis = 300, + durationMillis = 1000, + easing = EaseOutQuart, + ), + ) + Column( + modifier = modifier + .padding(horizontal = 16.dp, vertical = 10.dp), + ) { + Text( + text = stringResource(ContributorsRes.string.contributor_total), + style = MaterialTheme.typography.titleMedium, + ) + Row( + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.Bottom, + ) { + Text( + text = "$animatedTotalContributor", + style = MaterialTheme.typography.headlineLarge, + ) + Text( + text = stringResource(ContributorsRes.string.contributor_person), + style = MaterialTheme.typography.headlineSmall, + ) + } + Spacer(modifier = Modifier.height(16.dp)) + HorizontalDivider() + } +} From 0e58d2105b997bff966c91b30baa621e6cc38bfc Mon Sep 17 00:00:00 2001 From: Md Ahsan Ullah Rasel Date: Fri, 6 Sep 2024 12:09:46 +0900 Subject: [PATCH 3/6] Add Robo test for contributors count item --- .../confsched/testing/robot/ContributorsScreenRobot.kt | 10 ++++++++++ .../confsched/contributors/ContributorsScreenTest.kt | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/ContributorsScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/ContributorsScreenRobot.kt index 97177145a..087574c02 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/ContributorsScreenRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/ContributorsScreenRobot.kt @@ -5,10 +5,12 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.isDisplayed import androidx.compose.ui.test.performScrollToIndex import io.github.droidkaigi.confsched.contributors.ContributorsItemTestTagPrefix import io.github.droidkaigi.confsched.contributors.ContributorsScreen import io.github.droidkaigi.confsched.contributors.ContributorsTestTag +import io.github.droidkaigi.confsched.contributors.ContributorsTotalCountTestTag import io.github.droidkaigi.confsched.contributors.component.ContributorsItemImageTestTagPrefix import io.github.droidkaigi.confsched.contributors.component.ContributorsUserNameTextTestTagPrefix import io.github.droidkaigi.confsched.droidkaigiui.Inject @@ -80,6 +82,14 @@ class ContributorsScreenRobot @Inject constructor( .assertCountAtLeast(2) } + fun checkContributorTotalCountDisplayed() { + // Check contributors total count is being displayed + composeTestRule + .onNode(hasTestTag(ContributorsTotalCountTestTag)) + .assertExists() + .isDisplayed() + } + fun checkDoesNotFirstContributorItemDisplayed() { val contributor = Contributor.fakes().first() composeTestRule diff --git a/feature/contributors/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenTest.kt b/feature/contributors/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenTest.kt index 14b9d77d2..be89a0665 100644 --- a/feature/contributors/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenTest.kt +++ b/feature/contributors/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/contributors/ContributorsScreenTest.kt @@ -50,6 +50,11 @@ class ContributorsScreenTest(private val testCase: DescribedBehavior Date: Fri, 6 Sep 2024 13:08:16 +0900 Subject: [PATCH 4/6] Add string resources for iOS part --- .../Resources/Localizable.xcstrings | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app-ios/Sources/ContributorFeature/Resources/Localizable.xcstrings b/app-ios/Sources/ContributorFeature/Resources/Localizable.xcstrings index b2ad6f9c7..e7f58f053 100644 --- a/app-ios/Sources/ContributorFeature/Resources/Localizable.xcstrings +++ b/app-ios/Sources/ContributorFeature/Resources/Localizable.xcstrings @@ -16,7 +16,39 @@ } } } + }, + "Person" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "persons" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "人" + } + } + } + }, + "Total" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "合計" + } + } + } } }, "version" : "1.0" -} \ No newline at end of file +} From d5513a180816347bf8d5ec377e0888d6dfef7a3c Mon Sep 17 00:00:00 2001 From: Md Ahsan Ullah Rasel Date: Fri, 6 Sep 2024 13:08:47 +0900 Subject: [PATCH 5/6] Implement contributors count item in SwiftUI view for iOS --- .../ContributorsCountItem.swift | 49 +++++++++++++++++++ .../KmpPresenterContributorView.swift | 6 +++ .../SwiftUIContributorView.swift | 6 +++ 3 files changed, 61 insertions(+) create mode 100644 app-ios/Sources/ContributorFeature/ContributorsCountItem.swift diff --git a/app-ios/Sources/ContributorFeature/ContributorsCountItem.swift b/app-ios/Sources/ContributorFeature/ContributorsCountItem.swift new file mode 100644 index 000000000..5d7948b23 --- /dev/null +++ b/app-ios/Sources/ContributorFeature/ContributorsCountItem.swift @@ -0,0 +1,49 @@ +import SwiftUI +import Theme + +struct ContributorsCountItem: View { + let totalContributor: Int + let duration: Double = 1.0 + @State private var tracker = 0 + + var body: some View { + VStack(alignment: .leading) { + + Text(String(localized: "Total", bundle: .module)) + .textStyle(.titleMedium) + .foregroundStyle(AssetColors.Surface.onSurfaceVariant.swiftUIColor) + .frame(maxWidth: .infinity, alignment: .leading) + + HStack { + Text("\(tracker)") + .textStyle(.headlineLarge) + .foregroundStyle(AssetColors.Surface.onSurface.swiftUIColor) + .onAppear() { + animate() + } + + Text(String(localized: "Person", bundle: .module)) + .textStyle(.headlineSmall) + .foregroundStyle(AssetColors.Surface.onSurface.swiftUIColor) + } + + Spacer() + .frame(height: 16) + Divider() + } + } + + func animate() { + let interval = duration / Double(totalContributor) + Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { time in + tracker += 1 + if tracker == totalContributor { + time.invalidate() + } + } + } +} + +#Preview { + ContributorsCountItem(totalContributor: 112) +} diff --git a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift index 7e354a2c4..a11e8dc21 100644 --- a/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift +++ b/app-ios/Sources/ContributorFeature/KmpPresenterContributorView.swift @@ -24,6 +24,12 @@ struct KmpPresenterContributorView: View { if let contributors = currentState.map(\.contributors) { ScrollView { LazyVStack(spacing: 0) { + + ContributorsCountItem(totalContributor: contributors.count) + .frame(maxWidth: .infinity) + .padding(.horizontal, 16) + .padding(.vertical, 10) + ForEach(contributors, id: \.id) { value in let contributor = Model.Contributor( id: Int(value.id), diff --git a/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift b/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift index 7fde5d6bb..9d6a1f565 100644 --- a/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift +++ b/app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift @@ -14,6 +14,12 @@ struct SwiftUIContributorView: View { if let contributors = store.contributors { ScrollView { LazyVStack(spacing: 0) { + + ContributorsCountItem(totalContributor: contributors.count) + .frame(maxWidth: .infinity) + .padding(.horizontal, 16) + .padding(.vertical, 10) + ForEach(contributors, id: \.id) { contributor in ContributorListItemView(contributor: contributor) { url in store.send(.view(.contributorButtonTapped(url))) From 14300ef4bde13e60770c0489a4bdfa3aeb204f68 Mon Sep 17 00:00:00 2001 From: Md Ahsan Ullah Rasel Date: Fri, 6 Sep 2024 15:07:44 +0900 Subject: [PATCH 6/6] Update compose animation according to new UI code --- .../contributors/component/ContributorsCountItem.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt index 1d4130119..33f41b2d6 100644 --- a/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt +++ b/feature/contributors/src/commonMain/kotlin/io/github/droidkaigi/confsched/contributors/component/ContributorsCountItem.kt @@ -13,12 +13,15 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import conference_app_2024.feature.contributors.generated.resources.contributor_person -import conference_app_2024.feature.contributors.generated.resources.contributor_title import conference_app_2024.feature.contributors.generated.resources.contributor_total import io.github.droidkaigi.confsched.contributors.ContributorsRes import org.jetbrains.compose.resources.stringResource @@ -28,14 +31,18 @@ internal fun ContributorsCountItem( totalContributor: Int, modifier: Modifier = Modifier, ) { + var targetValue by remember { mutableStateOf(0) } val animatedTotalContributor by animateIntAsState( - targetValue = totalContributor, + targetValue = targetValue, animationSpec = tween( delayMillis = 300, durationMillis = 1000, easing = EaseOutQuart, ), ) + LaunchedEffect(totalContributor) { + targetValue = totalContributor + } Column( modifier = modifier .padding(horizontal = 16.dp, vertical = 10.dp),