From b282e47213dbf78feb9e2effd146bfa4b2d8de9b Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Mon, 27 Jun 2022 22:42:19 +0530 Subject: [PATCH] all: reorganize some composables --- android/build.gradle.kts | 1 + .../msfjarvis/claw/android/ui/LobstersApp.kt | 3 +- .../dev/msfjarvis/claw/android/ui/ext.kt | 41 ++++-------------- .../claw/android/ui/lists/DatabasePosts.kt | 4 +- .../claw/android/viewmodel/ClawViewModel.kt | 8 ++-- common/build.gradle.kts | 1 + .../claw/common}/ui/decorations/ClawAppBar.kt | 2 +- .../claw/common/ui/decorations/MonthHeader.kt | 12 ++--- .../dev/msfjarvis/claw/common/ui/ext.kt | 27 ++++++++++++ gradle/libs.versions.toml | 1 + paparazzi-tests/build.gradle.kts | 4 +- .../claw/android/tests/MonthHeaderTest.kt | 4 +- ...ndroid.tests_MonthHeaderTest_dark_mode.png | Bin 8935 -> 8889 bytes ...droid.tests_MonthHeaderTest_light_mode.png | Bin 8494 -> 8484 bytes 14 files changed, 56 insertions(+), 52 deletions(-) rename {android/src/main/kotlin/dev/msfjarvis/claw/android => common/src/commonMain/kotlin/dev/msfjarvis/claw/common}/ui/decorations/ClawAppBar.kt (92%) rename android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/StickyMonthHeader.kt => common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/MonthHeader.kt (72%) create mode 100644 common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/ext.kt diff --git a/android/build.gradle.kts b/android/build.gradle.kts index e3890973..fd361215 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.material.motion.navigation) implementation(libs.sqldelight.extensions.coroutines) implementation(libs.kotlin.coroutines.core) + implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.json) implementation(libs.retrofit.kotlinxSerializationConverter) implementation(libs.androidx.work.runtime.ktx) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt index fa835cf1..5ad1b51b 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt @@ -29,7 +29,6 @@ import androidx.navigation.navDeepLink import androidx.paging.compose.collectAsLazyPagingItems import com.google.accompanist.systemuicontroller.rememberSystemUiController import dev.msfjarvis.claw.android.R -import dev.msfjarvis.claw.android.ui.decorations.ClawAppBar import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar import dev.msfjarvis.claw.android.ui.decorations.NavigationItem import dev.msfjarvis.claw.android.ui.lists.DatabasePosts @@ -42,6 +41,8 @@ import dev.msfjarvis.claw.common.comments.HTMLConverter import dev.msfjarvis.claw.common.comments.LocalHTMLConverter import dev.msfjarvis.claw.common.res.ClawIcons import dev.msfjarvis.claw.common.theme.LobstersTheme +import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar +import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation import dev.msfjarvis.claw.common.urllauncher.UrlLauncher import dev.msfjarvis.claw.common.user.UserProfile import kotlinx.coroutines.launch diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt index 09e5bb97..10612d50 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt @@ -11,16 +11,12 @@ import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.ColorScheme -import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.unit.dp import androidx.navigation.NavController import dev.msfjarvis.claw.android.ui.navigation.Destinations import dev.msfjarvis.claw.android.viewmodel.ClawViewModel @@ -29,11 +25,10 @@ import dev.msfjarvis.claw.common.theme.DarkThemeColors import dev.msfjarvis.claw.common.theme.LightThemeColors import dev.msfjarvis.claw.common.urllauncher.UrlLauncher import dev.msfjarvis.claw.database.local.SavedPost -import java.text.SimpleDateFormat -import java.time.ZoneId -import java.time.ZonedDateTime -import java.util.Locale -import kotlin.math.ln +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime private const val AnimationDuration = 100 @@ -54,13 +49,11 @@ fun slideOutAnimation(): ExitTransition { } /** - * Parses a given [String] into a [ZonedDateTime]. This method only works on dates in the format - * returned by the Lobsters API, and is not a general purpose parsing solution. + * Parses a given [String] into a [LocalDateTime]. This method is only intended to be used for dates + * in the format returned by the Lobsters API, and is not a general purpose parsing solution. */ -fun String.asZonedDateTime(): ZonedDateTime { - val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US) - val date = checkNotNull(sdf.parse(this)) - return date.toInstant().atZone(ZoneId.systemDefault()) +fun String.toLocalDateTime(): LocalDateTime { + return Instant.parse(this).toLocalDateTime(TimeZone.currentSystemDefault()) } // The destination needs to be tracked like this rather than used directly since @@ -124,21 +117,3 @@ fun rememberPostActions( } } } - -/** - * Returns the [ColorScheme.surface] color with an alpha of the [ColorScheme.primary] color overlaid - * on top of it. Computes the surface tonal color at different elevation levels e.g. surface1 - * through surface5. - * - * Stolen from AndroidX, keep in sync when upgrading Compose. This version is hard-coded to - * replicate the logic used by the Material3 NavigationBar to determine its surface color. - * https://github.com/androidx/androidx/blob/74d3510b608c3cc26b9cf9be8d15a6a6c26192c2/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt#L453-L466 - */ -@Composable -fun ColorScheme.surfaceColorAtNavigationBarElevation(): Color { - // Absolute tonal elevation + NavigationBarTokens.ContainerElevation - val elevation = LocalAbsoluteTonalElevation.current + 3.0.dp - if (elevation == 0.dp) return surface - val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f - return primary.copy(alpha = alpha).compositeOver(surface) -} diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt index bd4dd90c..bddb046c 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/lists/DatabasePosts.kt @@ -6,11 +6,11 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import dev.msfjarvis.claw.android.ui.decorations.MonthHeader import dev.msfjarvis.claw.common.posts.PostActions import dev.msfjarvis.claw.common.ui.Divider +import dev.msfjarvis.claw.common.ui.decorations.MonthHeader import dev.msfjarvis.claw.database.local.SavedPost -import java.time.Month +import kotlinx.datetime.Month @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt index 30bc1ac5..6f9a737c 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/viewmodel/ClawViewModel.kt @@ -6,16 +6,16 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import dagger.hilt.android.lifecycle.HiltViewModel import dev.msfjarvis.claw.android.paging.LobstersPagingSource -import dev.msfjarvis.claw.android.ui.asZonedDateTime +import dev.msfjarvis.claw.android.ui.toLocalDateTime import dev.msfjarvis.claw.api.LobstersApi import dev.msfjarvis.claw.database.local.SavedPost -import java.time.Month import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.datetime.Month @HiltViewModel class ClawViewModel @@ -48,8 +48,8 @@ constructor( get() = savedPostsFlow.map(::mapSavedPosts) private fun mapSavedPosts(items: List): Map> { - val sorted = items.sortedByDescending { post -> post.createdAt.asZonedDateTime() } - return sorted.groupBy { post -> post.createdAt.asZonedDateTime().month } + val sorted = items.sortedByDescending { post -> post.createdAt.toLocalDateTime() } + return sorted.groupBy { post -> post.createdAt.toLocalDateTime().month } } suspend fun isPostSaved(post: SavedPost): Boolean { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 0fc0502d..0bbd653a 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { api(projects.model) api(libs.napier) implementation(libs.kotlin.coroutines.core) + implementation(libs.kotlinx.datetime) implementation(libs.compose.richtext.markdown) implementation(libs.compose.richtext.material3) implementation(libs.compose.richtext.ui) diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/ClawAppBar.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/ClawAppBar.kt similarity index 92% rename from android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/ClawAppBar.kt rename to common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/ClawAppBar.kt index b78785bb..024e729c 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/ClawAppBar.kt +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/ClawAppBar.kt @@ -1,4 +1,4 @@ -package dev.msfjarvis.claw.android.ui.decorations +package dev.msfjarvis.claw.common.ui.decorations import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.TopAppBarDefaults diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/StickyMonthHeader.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/MonthHeader.kt similarity index 72% rename from android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/StickyMonthHeader.kt rename to common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/MonthHeader.kt index f597471c..1352d967 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/decorations/StickyMonthHeader.kt +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/decorations/MonthHeader.kt @@ -1,4 +1,4 @@ -package dev.msfjarvis.claw.android.ui.decorations +package dev.msfjarvis.claw.common.ui.decorations import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -9,11 +9,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.unit.dp -import dev.msfjarvis.claw.android.ui.surfaceColorAtNavigationBarElevation -import java.time.Month -import java.time.format.TextStyle as JTextStyle -import java.util.Locale +import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation +import kotlinx.datetime.Month @Composable fun MonthHeader(month: Month) { @@ -23,7 +23,7 @@ fun MonthHeader(month: Month) { .background(MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation()) ) { Text( - text = month.getDisplayName(JTextStyle.FULL, Locale.getDefault()), + text = month.name.lowercase().capitalize(Locale.current), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(horizontal = 12.dp, vertical = 12.dp), ) diff --git a/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/ext.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/ext.kt new file mode 100644 index 00000000..c89f4837 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/ui/ext.kt @@ -0,0 +1,27 @@ +package dev.msfjarvis.claw.common.ui + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.LocalAbsoluteTonalElevation +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.compositeOver +import androidx.compose.ui.unit.dp +import kotlin.math.ln + +/** + * Returns the [ColorScheme.surface] color with an alpha of the [ColorScheme.primary] color overlaid + * on top of it. Computes the surface tonal color at different elevation levels e.g. surface1 + * through surface5. + * + * Stolen from AndroidX, keep in sync when upgrading Compose. This version is hard-coded to + * replicate the logic used by the Material3 NavigationBar to determine its surface color. + * https://github.com/androidx/androidx/blob/74d3510b608c3cc26b9cf9be8d15a6a6c26192c2/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt#L453-L466 + */ +@Composable +fun ColorScheme.surfaceColorAtNavigationBarElevation(): Color { + // Absolute tonal elevation + NavigationBarTokens.ContainerElevation + val elevation = LocalAbsoluteTonalElevation.current + 3.0.dp + if (elevation == 0.dp) return surface + val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f + return primary.copy(alpha = alpha).compositeOver(surface) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f8c0faf..d7f65989 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -53,6 +53,7 @@ dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref javapoet = "com.squareup:javapoet:1.13.0" kamel-image = "com.alialbaali.kamel:kamel-image:0.3.0" kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } +kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0" kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" } material_motion-core = { module = "io.github.fornewid:material-motion-compose-core", version.ref = "material_motion" } diff --git a/paparazzi-tests/build.gradle.kts b/paparazzi-tests/build.gradle.kts index 362e3834..e846b80f 100644 --- a/paparazzi-tests/build.gradle.kts +++ b/paparazzi-tests/build.gradle.kts @@ -15,10 +15,8 @@ android { dependencies { testImplementation(kotlin("test-junit")) - testImplementation(projects.android) + testImplementation(libs.kotlinx.datetime) testImplementation(projects.common) - testImplementation(projects.model) - testImplementation(projects.database) } tasks.withType().configureEach { diff --git a/paparazzi-tests/src/test/kotlin/dev/msfjarvis/claw/android/tests/MonthHeaderTest.kt b/paparazzi-tests/src/test/kotlin/dev/msfjarvis/claw/android/tests/MonthHeaderTest.kt index ca2a0faa..57599a01 100644 --- a/paparazzi-tests/src/test/kotlin/dev/msfjarvis/claw/android/tests/MonthHeaderTest.kt +++ b/paparazzi-tests/src/test/kotlin/dev/msfjarvis/claw/android/tests/MonthHeaderTest.kt @@ -2,10 +2,10 @@ package dev.msfjarvis.claw.android.tests import androidx.compose.material3.MaterialTheme import app.cash.paparazzi.Paparazzi -import dev.msfjarvis.claw.android.ui.decorations.MonthHeader import dev.msfjarvis.claw.common.theme.DarkThemeColors import dev.msfjarvis.claw.common.theme.LightThemeColors -import java.time.Month +import dev.msfjarvis.claw.common.ui.decorations.MonthHeader +import kotlinx.datetime.Month import org.junit.Rule import org.junit.Test diff --git a/paparazzi-tests/src/test/snapshots/images/dev.msfjarvis.claw.android.tests_MonthHeaderTest_dark_mode.png b/paparazzi-tests/src/test/snapshots/images/dev.msfjarvis.claw.android.tests_MonthHeaderTest_dark_mode.png index b0b2dd1166c6281c3ad0744691661c98edade11c..8f572f137b70b9e79493800e5344549a844f83d8 100644 GIT binary patch literal 8889 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

P%B;gn?tNdsdE&0qPel#lf1V1T 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 w_h;^wHUBFbxJ@J&7(5JVLugzopr0AJyj+5i9m diff --git a/paparazzi-tests/src/test/snapshots/images/dev.msfjarvis.claw.android.tests_MonthHeaderTest_light_mode.png b/paparazzi-tests/src/test/snapshots/images/dev.msfjarvis.claw.android.tests_MonthHeaderTest_light_mode.png index 39a5a87304e720cb4f43ffca1fcaf2628c3bf06f..80c58f11ce865022a32dc145cd0db02eb6d7efb9 100644 GIT binary patch literal 8484 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 literal 8494 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 Bsm=fZ