Skip to content

Commit

Permalink
Home screen UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
ychescale9 committed Nov 29, 2023
1 parent 6df71d6 commit 2f760fa
Show file tree
Hide file tree
Showing 29 changed files with 1,406 additions and 237 deletions.
5 changes: 3 additions & 2 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ plugins {
id("kstreamlined.android.application.compose")
id("kstreamlined.ksp")
id("com.google.dagger.hilt.android")
id("com.google.gms.google-services") apply false
id("com.google.firebase.firebase-perf")
id("com.google.firebase.crashlytics")
id("com.google.firebase.appdistribution")
Expand Down Expand Up @@ -251,10 +250,12 @@ dependencies {
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)

// Image loading
implementation(libs.coil.svg)

// SQLDelight
implementation(libs.sqldelight.androidDriver)


// LeakCanary
debugImplementation(libs.leakcanary.android)
implementation(libs.leakcanary.plumber)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.reactivecircus.kstreamlined.android.di
import android.content.Context
import android.os.Build
import coil.ImageLoader
import coil.decode.SvgDecoder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -18,6 +19,9 @@ object AppModule {
@Singleton
fun imageLoader(@ApplicationContext context: Context): ImageLoader {
return ImageLoader.Builder(context)
.components {
add(SvgDecoder.Factory())
}
.crossfade(enable = true)
// only enable hardware bitmaps on API 28+. See: https://github.com/coil-kt/coil/issues/159
.allowHardware(enable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
Expand Down
6 changes: 3 additions & 3 deletions android/common-ui/feed/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ plugins {

android {
namespace = "io.github.reactivecircus.kstreamlined.android.common.ui.feed"
buildFeatures {
androidResources = true
}
}

androidComponents {
Expand All @@ -24,4 +21,7 @@ dependencies {
// Compose
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.ui.tooling)

// Image loading
implementation(libs.coil)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.reactivecircus.kstreamlined.android.common.ui.feed

import androidx.compose.runtime.Immutable
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem

@Immutable
public data class DisplayableFeedItem<T : FeedItem>(
val value: T,
val displayablePublishTime: String,
)

public fun <T : FeedItem> T.toDisplayable(
displayablePublishTime: String,
): DisplayableFeedItem<T> {
return DisplayableFeedItem(
value = this,
displayablePublishTime = displayablePublishTime,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package io.github.reactivecircus.kstreamlined.android.common.ui.feed

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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import io.github.reactivecircus.kstreamlined.android.designsystem.ThemePreviews
import io.github.reactivecircus.kstreamlined.android.designsystem.component.IconButton
import io.github.reactivecircus.kstreamlined.android.designsystem.component.Surface
import io.github.reactivecircus.kstreamlined.android.designsystem.component.Text
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KSTheme
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.BookmarkAdd
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.BookmarkFill
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.KSIcons
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import kotlinx.datetime.toInstant

@Composable
public fun KotlinBlogCard(
item: DisplayableFeedItem<FeedItem.KotlinBlog>,
onItemClick: (FeedItem.KotlinBlog) -> Unit,
onSaveButtonClick: (FeedItem.KotlinBlog) -> Unit,
modifier: Modifier = Modifier,
) {
Surface(
onClick = { onItemClick(item.value) },
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
color = KSTheme.colorScheme.container,
contentColor = KSTheme.colorScheme.onBackground,
) {
Column {
AsyncImage(
model = item.value.featuredImageUrl,
contentDescription = item.value.title,
modifier = Modifier
.fillMaxWidth()
.height(ImageHeight),
contentScale = ContentScale.FillWidth,
)

Column(
modifier = Modifier.padding(
start = 16.dp,
end = 8.dp,
top = 24.dp,
bottom = 8.dp,
),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = item.value.title,
style = KSTheme.typography.titleMedium,
modifier = Modifier.padding(end = 8.dp),
overflow = TextOverflow.Ellipsis,
)
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = item.displayablePublishTime,
style = KSTheme.typography.bodyMedium,
color = KSTheme.colorScheme.onBackgroundVariant,
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
if (item.value.savedForLater) {
KSIcons.BookmarkFill
} else {
KSIcons.BookmarkAdd
},
contentDescription = null,
onClick = { onSaveButtonClick(item.value) },
modifier = Modifier,
)
}
}
}
}
}

private val ImageHeight = 200.dp

@Composable
@ThemePreviews
private fun PreviewKotlinBlogCard_unsaved() {
KSTheme {
Surface {
KotlinBlogCard(
item = FeedItem.KotlinBlog(
id = "1",
title = "Kotlin Multiplatform Development Roadmap for 2024",
publishTime = "2023-11-16T11:59:46Z".toInstant(),
contentUrl = "contentUrl",
savedForLater = false,
featuredImageUrl = "",
).toDisplayable("Moments ago"),
onItemClick = {},
onSaveButtonClick = {},
modifier = Modifier.padding(24.dp),
)
}
}
}

@Composable
@ThemePreviews
private fun PreviewKotlinBlogCard_saved() {
KSTheme {
Surface {
KotlinBlogCard(
item = FeedItem.KotlinBlog(
id = "1",
title = "Kotlin Multiplatform Development Roadmap for 2024",
publishTime = "2023-11-16T11:59:46Z".toInstant(),
contentUrl = "contentUrl",
savedForLater = true,
featuredImageUrl = "",
).toDisplayable("Moments ago"),
onItemClick = {},
onSaveButtonClick = {},
modifier = Modifier.padding(24.dp),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import io.github.reactivecircus.kstreamlined.android.designsystem.ThemePreviews
Expand All @@ -20,21 +23,33 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.ico
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.BookmarkFill
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.KSIcons
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import kotlinx.datetime.toInstant

@Composable
public fun KotlinWeeklyCard(
item: FeedItem.KotlinWeekly,
item: DisplayableFeedItem<FeedItem.KotlinWeekly>,
onItemClick: (FeedItem.KotlinWeekly) -> Unit,
onSaveButtonClick: (FeedItem.KotlinWeekly) -> Unit,
modifier: Modifier = Modifier,
) {
val brush = Brush.horizontalGradient(
colors = listOf(
KSTheme.colorScheme.primaryDark,
KSTheme.colorScheme.primaryLight,
),
)
Surface(
onClick = { onItemClick(item) },
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
color = KSTheme.colorScheme.primary,
onClick = { onItemClick(item.value) },
modifier = modifier
.drawBehind {
drawRoundRect(
brush = brush,
cornerRadius = CornerRadius(16.dp.toPx(), 16.dp.toPx()),
)
}
.fillMaxWidth(),
color = Color.Transparent,
contentColor = KSTheme.colorScheme.onPrimary,
elevation = 4.dp,
) {
Row(
modifier = Modifier.padding(vertical = 24.dp),
Expand All @@ -44,27 +59,28 @@ public fun KotlinWeeklyCard(
modifier = Modifier
.weight(1f)
.padding(start = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = item.title,
text = item.value.title,
style = KSTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = item.publishTime,
text = item.displayablePublishTime,
style = KSTheme.typography.bodyMedium,
color = KSTheme.colorScheme.onPrimaryVariant,
)
}
IconButton(
if (item.savedForLater) {
if (item.value.savedForLater) {
KSIcons.BookmarkFill
} else {
KSIcons.BookmarkAdd
},
contentDescription = null,
onClick = { onSaveButtonClick(item) },
onClick = { onSaveButtonClick(item.value) },
modifier = Modifier.padding(end = 8.dp),
)
}
Expand All @@ -80,10 +96,10 @@ private fun PreviewKotlinWeeklyCard_unsaved() {
item = FeedItem.KotlinWeekly(
id = "1",
title = "Kotlin Weekly #381",
publishTime = "Moments ago",
publishTime = "2023-11-19T09:13:00Z".toInstant(),
contentUrl = "contentUrl",
savedForLater = false,
),
).toDisplayable(displayablePublishTime = "3 hours ago"),
onItemClick = {},
onSaveButtonClick = {},
modifier = Modifier.padding(24.dp),
Expand All @@ -101,10 +117,10 @@ private fun PreviewKotlinWeeklyCard_saved() {
item = FeedItem.KotlinWeekly(
id = "1",
title = "Kotlin Weekly #381",
publishTime = "3 hours ago",
publishTime = "2023-11-19T09:13:00Z".toInstant(),
contentUrl = "contentUrl",
savedForLater = true,
),
).toDisplayable(displayablePublishTime = "3 hours ago"),
onItemClick = {},
onSaveButtonClick = {},
modifier = Modifier.padding(24.dp),
Expand Down
Loading

0 comments on commit 2f760fa

Please sign in to comment.