Skip to content

Commit

Permalink
Home feed item transformation, modules restructuring.
Browse files Browse the repository at this point in the history
  • Loading branch information
ychescale9 committed Dec 8, 2023
1 parent 04b06f5 commit 6edda72
Show file tree
Hide file tree
Showing 20 changed files with 277 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KST
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.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.datetime.toInstant

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KST
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.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.datetime.toInstant

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KST
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.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.datetime.toInstant

@OptIn(ExperimentalFoundationApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KST
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.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.datetime.toInstant

@Composable
Expand Down
1 change: 0 additions & 1 deletion android/feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ dependencies {
implementation(project(":feature:common"))
implementation(project(":common-ui:feed"))
implementation(project(":kmp:presentation:home"))
implementation(project(":kmp:data"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinBlogCa
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinWeeklyCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinYouTubeCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.TalkingKotlinCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.toDisplayable
import io.github.reactivecircus.kstreamlined.android.designsystem.component.FilledIconButton
import io.github.reactivecircus.kstreamlined.android.designsystem.component.Surface
import io.github.reactivecircus.kstreamlined.android.designsystem.component.Text
Expand All @@ -37,6 +36,7 @@ import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.ico
import io.github.reactivecircus.kstreamlined.android.feature.home.component.FeedFilterChip
import io.github.reactivecircus.kstreamlined.android.feature.home.component.SyncButton
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.coroutines.delay
import kotlinx.datetime.toInstant

Expand Down
1 change: 0 additions & 1 deletion android/feature/saved-for-later/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ dependencies {
implementation(project(":feature:common"))
implementation(project(":common-ui:feed"))
implementation(project(":kmp:presentation:saved-for-later"))
implementation(project(":kmp:data"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinBlogCa
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinWeeklyCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.KotlinYouTubeCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.TalkingKotlinCard
import io.github.reactivecircus.kstreamlined.android.common.ui.feed.toDisplayable
import io.github.reactivecircus.kstreamlined.android.designsystem.component.FilledIconButton
import io.github.reactivecircus.kstreamlined.android.designsystem.component.TopNavBar
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.KSTheme
import io.github.reactivecircus.kstreamlined.android.designsystem.foundation.icon.KSIcons
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import kotlinx.datetime.toInstant

@Composable
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ androidx-test-ext-junit = "1.1.5"
androidx-test-espresso = "3.5.1"
androidx-test-uiautomator = "2.2.0"
coil = "2.5.0"
jetbrainsCompose = "1.5.11"
kermit = "2.0.2"
radiography = "2.5"
sqldelight = "2.0.1"
Expand Down Expand Up @@ -110,6 +111,7 @@ androidx-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib"
androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "androidx-test-espresso" }
coil = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
jetbrainsComposeRuntime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "jetbrainsCompose" }
apollo-runtime = { module = "com.apollographql.apollo3:apollo-runtime", version.ref = "apollo" }
apollo-normalizedCache = { module = "com.apollographql.apollo3:apollo-normalized-cache", version.ref = "apollo" }
apollo-adapters = { module = "com.apollographql.apollo3:apollo-adapters", version.ref = "apollo" }
Expand Down
1 change: 1 addition & 0 deletions kmp/model/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ kotlin {
commonMain {
dependencies {
api(libs.kotlinx.datetime)
implementation(libs.jetbrainsComposeRuntime)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.github.reactivecircus.kstreamlined.android.common.ui.feed
package io.github.reactivecircus.kstreamlined.kmp.model.feed

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

@Immutable
public data class DisplayableFeedItem<T : FeedItem>(
Expand Down
4 changes: 4 additions & 0 deletions kmp/presentation/common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
plugins {
id("kstreamlined.kmp.common")
id("kstreamlined.kmp.test")
}
3 changes: 3 additions & 0 deletions kmp/presentation/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":kmp:presentation:common"))
implementation(project(":kmp:data"))
implementation(project(":kmp:pretty-time"))
api(project(":kmp:model"))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.reactivecircus.kstreamlined.kmp.presentation.home

import io.github.reactivecircus.kstreamlined.kmp.model.feed.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem

internal sealed interface HomeFeedItem {
data class SectionHeader(val title: String) : HomeFeedItem
data class Item(val displayableFeedItem: DisplayableFeedItem<FeedItem>) : HomeFeedItem
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.github.reactivecircus.kstreamlined.kmp.presentation.home

import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.toDisplayable
import io.github.reactivecircus.kstreamlined.kmp.prettytime.timeAgo
import io.github.reactivecircus.kstreamlined.kmp.prettytime.weeksAgo
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone

internal fun List<FeedItem>.toHomeFeedItems(
clock: Clock = Clock.System,
timeZone: TimeZone = TimeZone.currentSystemDefault(),
): List<HomeFeedItem> {
val homeFeedItems = mutableListOf<HomeFeedItem>()
var currentSectionHeader: String? = null

// assume items are already sorted by publish time in descending order
forEach { feedItem ->
val sectionHeader = feedItem.publishTime.weeksAgo(clock)
println("${feedItem.publishTime}: \"$sectionHeader\" for item: $feedItem")
if (sectionHeader != currentSectionHeader) {
homeFeedItems.add(HomeFeedItem.SectionHeader(sectionHeader))
currentSectionHeader = sectionHeader
}
val displayableFeedItem = feedItem.toDisplayable(
feedItem.publishTime.timeAgo(clock, timeZone)
)
homeFeedItems.add(HomeFeedItem.Item(displayableFeedItem))
}

return homeFeedItems
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package io.github.reactivecircus.kstreamlined.kmp.presentation.home

import io.github.reactivecircus.kstreamlined.kmp.model.feed.DisplayableFeedItem
import io.github.reactivecircus.kstreamlined.kmp.model.feed.FeedItem
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlin.test.Test
import kotlin.test.assertEquals

class HomeFeedItemMapperTest {

@Test
fun `transformed HomeFeedItems are grouped by weeks with expected displayable time`() {
val fixedClock = object : Clock {
override fun now(): Instant {
return "2023-12-03T03:10:54Z".toInstant()
}
}
val timeZone = TimeZone.UTC

val feedItems = listOf(
// this week
// moments ago
FeedItem.KotlinBlog(
id = "1",
title = "Kotlin Blog 1",
publishTime = "2023-12-03T03:10:00Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
featuredImageUrl = "feature-image-url",
),
// 30 minutes ago
FeedItem.KotlinYouTube(
id = "2",
title = "Kotlin YouTube 1",
publishTime = "2023-12-03T02:40:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
thumbnailUrl = "thumbnail-url",
description = "description",
),
// 5 hours ago
FeedItem.KotlinWeekly(
id = "3",
title = "Kotlin Weekly 1",
publishTime = "2023-12-02T22:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
),
// yesterday
FeedItem.TalkingKotlin(
id = "4",
title = "Talking Kotlin 1",
publishTime = "2023-12-02T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
podcastLogoUrl = "podcast-logo-url",
),
// 5 days ago
FeedItem.KotlinBlog(
id = "5",
title = "Kotlin Blog 2",
publishTime = "2023-11-28T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
featuredImageUrl = "feature-image-url",
),
// last week
// 12 days ago
FeedItem.KotlinYouTube(
id = "6",
title = "Kotlin YouTube 2",
publishTime = "2023-11-21T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
thumbnailUrl = "thumbnail-url",
description = "description",
),
// earlier
// 20 days ago
FeedItem.KotlinWeekly(
id = "7",
title = "Kotlin Weekly 2",
publishTime = "2023-11-13T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
),
)

val expectedHomeFeedItems = listOf(
HomeFeedItem.SectionHeader("This week"),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinBlog(
id = "1",
title = "Kotlin Blog 1",
publishTime = "2023-12-03T03:10:00Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
featuredImageUrl = "feature-image-url",
),
displayablePublishTime = "Moments ago",
)
),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinYouTube(
id = "2",
title = "Kotlin YouTube 1",
publishTime = "2023-12-03T02:40:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
thumbnailUrl = "thumbnail-url",
description = "description",
),
displayablePublishTime = "30 minutes ago",
)
),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinWeekly(
id = "3",
title = "Kotlin Weekly 1",
publishTime = "2023-12-02T22:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
),
displayablePublishTime = "5 hours ago",
)
),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.TalkingKotlin(
id = "4",
title = "Talking Kotlin 1",
publishTime = "2023-12-02T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
podcastLogoUrl = "podcast-logo-url",
),
displayablePublishTime = "Yesterday",
)
),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinBlog(
id = "5",
title = "Kotlin Blog 2",
publishTime = "2023-11-28T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
featuredImageUrl = "feature-image-url",
),
displayablePublishTime = "5 days ago",
)
),
HomeFeedItem.SectionHeader("Last week"),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinYouTube(
id = "6",
title = "Kotlin YouTube 2",
publishTime = "2023-11-21T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
thumbnailUrl = "thumbnail-url",
description = "description",
),
displayablePublishTime = "21 Nov 2023",
)
),
HomeFeedItem.SectionHeader("Earlier"),
HomeFeedItem.Item(
displayableFeedItem = DisplayableFeedItem(
FeedItem.KotlinWeekly(
id = "7",
title = "Kotlin Weekly 2",
publishTime = "2023-11-13T03:10:54Z".toInstant(),
contentUrl = "content-url",
savedForLater = false,
),
displayablePublishTime = "13 Nov 2023",
)
),
)

val actualHomeFeedItems = feedItems.toHomeFeedItems(fixedClock, timeZone)

assertEquals(expectedHomeFeedItems, actualHomeFeedItems)
}
}
3 changes: 3 additions & 0 deletions kmp/presentation/saved-for-later/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":kmp:presentation:common"))
implementation(project(":kmp:data"))
implementation(project(":kmp:pretty-time"))
api(project(":kmp:model"))
}
}
}
Expand Down
Loading

0 comments on commit 6edda72

Please sign in to comment.