Skip to content

Commit 9f1d15b

Browse files
authored
Merge pull request #360 from OpenHub-Store/feat-recently-viewed
2 parents 1d7078c + dfd28d0 commit 9f1d15b

39 files changed

Lines changed: 823 additions & 13 deletions

File tree

composeApp/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ kotlin {
6464
implementation(projects.feature.starred.data)
6565
implementation(projects.feature.starred.presentation)
6666

67+
implementation(projects.feature.recentlyViewed.presentation)
68+
6769
implementation(libs.jetbrains.compose.navigation)
6870
implementation(libs.bundles.koin.common)
6971
implementation(libs.liquid)
207 Bytes
Binary file not shown.
198 Bytes
Binary file not shown.

composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/ViewModelsModule.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import zed.rainxch.devprofile.presentation.DeveloperProfileViewModel
99
import zed.rainxch.favourites.presentation.FavouritesViewModel
1010
import zed.rainxch.home.presentation.HomeViewModel
1111
import zed.rainxch.profile.presentation.ProfileViewModel
12+
import zed.rainxch.recentlyviewed.presentation.RecentlyViewedViewModel
1213
import zed.rainxch.search.presentation.SearchViewModel
1314
import zed.rainxch.starred.presentation.StarredReposViewModel
1415

@@ -20,6 +21,7 @@ val viewModelsModule =
2021
viewModelOf(::DeveloperProfileViewModel)
2122
viewModelOf(::FavouritesViewModel)
2223
viewModelOf(::HomeViewModel)
24+
viewModelOf(::RecentlyViewedViewModel)
2325
viewModelOf(::SearchViewModel)
2426
viewModelOf(::ProfileViewModel)
2527
viewModelOf(::StarredReposViewModel)

composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import zed.rainxch.devprofile.presentation.DeveloperProfileRoot
3737
import zed.rainxch.favourites.presentation.FavouritesRoot
3838
import zed.rainxch.home.presentation.HomeRoot
3939
import zed.rainxch.profile.presentation.ProfileRoot
40+
import zed.rainxch.recentlyviewed.presentation.RecentlyViewedRoot
4041
import zed.rainxch.search.presentation.SearchRoot
4142
import zed.rainxch.starred.presentation.StarredReposRoot
4243

@@ -247,6 +248,9 @@ fun AppNavigation(
247248
onNavigateToFavouriteRepos = {
248249
navController.navigate(GithubStoreGraph.FavouritesScreen)
249250
},
251+
onNavigateToRecentlyViewed = {
252+
navController.navigate(GithubStoreGraph.RecentlyViewedScreen)
253+
},
250254
onNavigateToDevProfile = { username ->
251255
navController.navigate(GithubStoreGraph.DeveloperProfileScreen(username))
252256
},
@@ -256,6 +260,28 @@ fun AppNavigation(
256260
)
257261
}
258262

263+
composable<GithubStoreGraph.RecentlyViewedScreen> {
264+
RecentlyViewedRoot(
265+
onNavigateBack = {
266+
navController.navigateUp()
267+
},
268+
onNavigateToDetails = { repoId ->
269+
navController.navigate(
270+
GithubStoreGraph.DetailsScreen(
271+
repositoryId = repoId,
272+
),
273+
)
274+
},
275+
onNavigateToDeveloperProfile = { username ->
276+
navController.navigate(
277+
GithubStoreGraph.DeveloperProfileScreen(
278+
username = username,
279+
),
280+
)
281+
},
282+
)
283+
}
284+
259285
composable<GithubStoreGraph.SponsorScreen> {
260286
zed.rainxch.profile.presentation.SponsorScreen(
261287
onNavigateBack = {

composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/GithubStoreGraph.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ sealed interface GithubStoreGraph {
3535
@Serializable
3636
data object StarredReposScreen : GithubStoreGraph
3737

38+
@Serializable
39+
data object RecentlyViewedScreen : GithubStoreGraph
40+
3841
@Serializable
3942
data object AppsScreen : GithubStoreGraph
4043

core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/6.json

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"formatVersion": 1,
33
"database": {
44
"version": 6,
5-
"identityHash": "ce64b232de8d6ab9d95e3adabf7c7c43",
5+
"identityHash": "3d6c862d777da61753a7876aca56a569",
66
"entities": [
77
{
88
"tableName": "installed_apps",
@@ -497,14 +497,48 @@
497497
},
498498
{
499499
"tableName": "seen_repos",
500-
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `seenAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))",
500+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `seenAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))",
501501
"fields": [
502502
{
503503
"fieldPath": "repoId",
504504
"columnName": "repoId",
505505
"affinity": "INTEGER",
506506
"notNull": true
507507
},
508+
{
509+
"fieldPath": "repoName",
510+
"columnName": "repoName",
511+
"affinity": "TEXT",
512+
"notNull": true
513+
},
514+
{
515+
"fieldPath": "repoOwner",
516+
"columnName": "repoOwner",
517+
"affinity": "TEXT",
518+
"notNull": true
519+
},
520+
{
521+
"fieldPath": "repoOwnerAvatarUrl",
522+
"columnName": "repoOwnerAvatarUrl",
523+
"affinity": "TEXT",
524+
"notNull": true
525+
},
526+
{
527+
"fieldPath": "repoDescription",
528+
"columnName": "repoDescription",
529+
"affinity": "TEXT"
530+
},
531+
{
532+
"fieldPath": "primaryLanguage",
533+
"columnName": "primaryLanguage",
534+
"affinity": "TEXT"
535+
},
536+
{
537+
"fieldPath": "repoUrl",
538+
"columnName": "repoUrl",
539+
"affinity": "TEXT",
540+
"notNull": true
541+
},
508542
{
509543
"fieldPath": "seenAt",
510544
"columnName": "seenAt",
@@ -522,7 +556,7 @@
522556
],
523557
"setupQueries": [
524558
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
525-
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ce64b232de8d6ab9d95e3adabf7c7c43')"
559+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3d6c862d777da61753a7876aca56a569')"
526560
]
527561
}
528562
}

core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/SeenRepoDao.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ interface SeenRepoDao {
1212
@Query("SELECT repoId FROM seen_repos")
1313
fun getAllSeenRepoIds(): Flow<List<Long>>
1414

15-
@Insert(onConflict = OnConflictStrategy.IGNORE)
15+
@Query("SELECT * FROM seen_repos ORDER BY seenAt DESC")
16+
fun getAllSeenRepos(): Flow<List<SeenRepoEntity>>
17+
18+
@Insert(onConflict = OnConflictStrategy.REPLACE)
1619
suspend fun insert(entity: SeenRepoEntity)
1720

21+
@Query("DELETE FROM seen_repos WHERE repoId = :repoId")
22+
suspend fun deleteById(repoId: Long)
23+
1824
@Query("DELETE FROM seen_repos")
1925
suspend fun clearAll()
2026
}

core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/entities/SeenRepoEntity.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ import androidx.room.PrimaryKey
77
data class SeenRepoEntity(
88
@PrimaryKey
99
val repoId: Long,
10+
val repoName: String,
11+
val repoOwner: String,
12+
val repoOwnerAvatarUrl: String,
13+
val repoDescription: String?,
14+
val primaryLanguage: String?,
15+
val repoUrl: String,
1016
val seenAt: Long,
1117
)

core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/SeenReposRepositoryImpl.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.map
55
import zed.rainxch.core.data.local.db.dao.SeenRepoDao
66
import zed.rainxch.core.data.local.db.entities.SeenRepoEntity
7+
import zed.rainxch.core.domain.model.GithubRepoSummary
8+
import zed.rainxch.core.domain.model.SeenRepo
79
import zed.rainxch.core.domain.repository.SeenReposRepository
810

911
class SeenReposRepositoryImpl(
@@ -12,16 +14,43 @@ class SeenReposRepositoryImpl(
1214
override fun getAllSeenRepoIds(): Flow<Set<Long>> =
1315
seenRepoDao.getAllSeenRepoIds().map { it.toSet() }
1416

15-
override suspend fun markAsSeen(repoId: Long) {
17+
override fun getAllSeenRepos(): Flow<List<SeenRepo>> =
18+
seenRepoDao.getAllSeenRepos().map { entities ->
19+
entities.map { it.toDomain() }
20+
}
21+
22+
override suspend fun markAsSeen(repo: GithubRepoSummary) {
1623
seenRepoDao.insert(
1724
SeenRepoEntity(
18-
repoId = repoId,
25+
repoId = repo.id,
26+
repoName = repo.name,
27+
repoOwner = repo.owner.login,
28+
repoOwnerAvatarUrl = repo.owner.avatarUrl,
29+
repoDescription = repo.description,
30+
primaryLanguage = repo.language,
31+
repoUrl = repo.htmlUrl,
1932
seenAt = System.currentTimeMillis(),
2033
),
2134
)
2235
}
2336

37+
override suspend fun removeFromHistory(repoId: Long) {
38+
seenRepoDao.deleteById(repoId)
39+
}
40+
2441
override suspend fun clearAll() {
2542
seenRepoDao.clearAll()
2643
}
44+
45+
private fun SeenRepoEntity.toDomain(): SeenRepo =
46+
SeenRepo(
47+
repoId = repoId,
48+
repoName = repoName,
49+
repoOwner = repoOwner,
50+
repoOwnerAvatarUrl = repoOwnerAvatarUrl,
51+
repoDescription = repoDescription,
52+
primaryLanguage = primaryLanguage,
53+
repoUrl = repoUrl,
54+
seenAt = seenAt,
55+
)
2756
}

0 commit comments

Comments
 (0)