Skip to content

Commit 0609157

Browse files
committed
refactor: implement UI models and immutable collections for search and home features
- Introduce `GithubRepoSummaryUi`, `GithubUserUi`, and `DiscoveryRepositoryUi` to separate domain models from the presentation layer. - Refactor `SearchState` and `HomeState` to use `ImmutableList` and `persistentListOf` for better performance and stability. - Add UI-specific enums (`ProgrammingLanguageUi`, `SearchPlatformUi`, `SortByUi`, `SortOrderUi`) and corresponding mappers to handle UI-to-domain transformations. - Update `RepositoryCard` and other UI components to consume the new UI models. - Refactor navigation to pass repository IDs instead of full summary objects. - Move `ParsedGithubLink` and `GithubUrlParser` into the search presentation model package. - Add `kotlinx.collections.immutable` dependency to core and search presentation modules.
1 parent a529e8e commit 0609157

32 files changed

Lines changed: 359 additions & 195 deletions

File tree

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ fun AppNavigation(navController: NavHostController) {
7171
onNavigateToApps = {
7272
navController.navigate(GithubStoreGraph.AppsScreen)
7373
},
74-
onNavigateToDetails = { repo ->
74+
onNavigateToDetails = { repoId ->
7575
navController.navigate(
7676
GithubStoreGraph.DetailsScreen(
77-
repositoryId = repo.id,
77+
repositoryId = repoId,
7878
),
7979
)
8080
},
@@ -93,10 +93,10 @@ fun AppNavigation(navController: NavHostController) {
9393
onNavigateBack = {
9494
navController.navigateUp()
9595
},
96-
onNavigateToDetails = { repo ->
96+
onNavigateToDetails = { repoId ->
9797
navController.navigate(
9898
GithubStoreGraph.DetailsScreen(
99-
repositoryId = repo.id,
99+
repositoryId = repoId,
100100
),
101101
)
102102
},

core/presentation/build.gradle.kts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,13 @@ kotlin {
1515

1616
implementation(libs.jetbrains.lifecycle.compose)
1717
implementation(libs.kotlinx.datetime)
18+
implementation(libs.kotlinx.collections.immutable)
1819

1920
implementation(compose.components.resources)
2021

2122
implementation(libs.androidx.compose.ui.tooling.preview)
2223
}
2324
}
24-
25-
androidMain {
26-
dependencies {
27-
}
28-
}
29-
30-
jvmMain {
31-
dependencies {
32-
}
33-
}
3425
}
3526
}
3627

core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ import androidx.compose.ui.text.style.TextOverflow
4141
import androidx.compose.ui.tooling.preview.Preview
4242
import androidx.compose.ui.unit.dp
4343
import org.jetbrains.compose.resources.stringResource
44-
import zed.rainxch.core.domain.model.GithubRepoSummary
45-
import zed.rainxch.core.domain.model.GithubUser
46-
import zed.rainxch.core.presentation.model.DiscoveryRepository
44+
import zed.rainxch.core.presentation.model.DiscoveryRepositoryUi
45+
import zed.rainxch.core.presentation.model.GithubRepoSummaryUi
46+
import zed.rainxch.core.presentation.model.GithubUserUi
4747
import zed.rainxch.core.presentation.theme.GithubStoreTheme
4848
import zed.rainxch.core.presentation.utils.formatReleasedAt
4949
import zed.rainxch.core.presentation.utils.hasWeekNotPassed
@@ -52,7 +52,7 @@ import zed.rainxch.githubstore.core.presentation.res.*
5252
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalLayoutApi::class)
5353
@Composable
5454
fun RepositoryCard(
55-
discoveryRepository: DiscoveryRepository,
55+
discoveryRepositoryUi: DiscoveryRepositoryUi,
5656
onClick: () -> Unit,
5757
onShareClick: () -> Unit,
5858
onDeveloperClick: (String) -> Unit,
@@ -65,7 +65,7 @@ fun RepositoryCard(
6565
modifier = modifier,
6666
) {
6767
Box {
68-
if (discoveryRepository.isFavourite) {
68+
if (discoveryRepositoryUi.isFavourite) {
6969
Icon(
7070
imageVector = Icons.Default.Favorite,
7171
contentDescription = null,
@@ -78,7 +78,7 @@ fun RepositoryCard(
7878
)
7979
}
8080

81-
if (discoveryRepository.isStarred) {
81+
if (discoveryRepositoryUi.isStarred) {
8282
Icon(
8383
imageVector = Icons.Default.Star,
8484
contentDescription = null,
@@ -104,21 +104,21 @@ fun RepositoryCard(
104104
Row(
105105
modifier =
106106
Modifier.clickable(onClick = {
107-
onDeveloperClick(discoveryRepository.repository.owner.login)
107+
onDeveloperClick(discoveryRepositoryUi.repository.owner.login)
108108
}),
109109
verticalAlignment = Alignment.CenterVertically,
110110
horizontalArrangement = Arrangement.spacedBy(12.dp),
111111
) {
112112
GitHubStoreImage(
113-
imageModel = { discoveryRepository.repository.owner.avatarUrl },
113+
imageModel = { discoveryRepositoryUi.repository.owner.avatarUrl },
114114
modifier =
115115
Modifier
116116
.size(32.dp)
117117
.clip(CircleShape),
118118
)
119119

120120
Text(
121-
text = discoveryRepository.repository.owner.login,
121+
text = discoveryRepositoryUi.repository.owner.login,
122122
style = MaterialTheme.typography.titleMedium,
123123
color = MaterialTheme.colorScheme.outline,
124124
maxLines = 1,
@@ -128,7 +128,7 @@ fun RepositoryCard(
128128
}
129129

130130
Text(
131-
text = "/ ${discoveryRepository.repository.name}",
131+
text = "/ ${discoveryRepositoryUi.repository.name}",
132132
style = MaterialTheme.typography.titleMedium,
133133
color = MaterialTheme.colorScheme.outline,
134134
softWrap = false,
@@ -145,7 +145,7 @@ fun RepositoryCard(
145145
horizontalArrangement = Arrangement.spacedBy(8.dp),
146146
) {
147147
Text(
148-
text = discoveryRepository.repository.name,
148+
text = discoveryRepositoryUi.repository.name,
149149
fontWeight = FontWeight.Bold,
150150
style = MaterialTheme.typography.titleLarge,
151151
color = MaterialTheme.colorScheme.onSurface,
@@ -155,14 +155,14 @@ fun RepositoryCard(
155155
modifier = Modifier.weight(1f, fill = false),
156156
)
157157

158-
if (discoveryRepository.repository.isFork) {
158+
if (discoveryRepositoryUi.repository.isFork) {
159159
ForkBadge()
160160
}
161161
}
162162

163163
Spacer(modifier = Modifier.height(4.dp))
164164

165-
discoveryRepository.repository.description?.let {
165+
discoveryRepositoryUi.repository.description?.let {
166166
Text(
167167
text = it,
168168
color = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -181,7 +181,7 @@ fun RepositoryCard(
181181
horizontalArrangement = Arrangement.spacedBy(16.dp),
182182
) {
183183
Text(
184-
text = "${discoveryRepository.repository.stargazersCount}",
184+
text = "${discoveryRepositoryUi.repository.stargazersCount}",
185185
style = MaterialTheme.typography.titleMedium,
186186
color = MaterialTheme.colorScheme.onSurfaceVariant,
187187
maxLines = 1,
@@ -190,15 +190,15 @@ fun RepositoryCard(
190190
)
191191

192192
Text(
193-
text = "• 🌴 ${discoveryRepository.repository.forksCount}",
193+
text = "• 🌴 ${discoveryRepositoryUi.repository.forksCount}",
194194
style = MaterialTheme.typography.titleMedium,
195195
color = MaterialTheme.colorScheme.onSurfaceVariant,
196196
maxLines = 1,
197197
softWrap = false,
198198
overflow = TextOverflow.Ellipsis,
199199
)
200200

201-
discoveryRepository.repository.language?.let {
201+
discoveryRepositoryUi.repository.language?.let {
202202
Text(
203203
text = "$it",
204204
style = MaterialTheme.typography.titleMedium,
@@ -210,22 +210,22 @@ fun RepositoryCard(
210210
}
211211
}
212212

213-
if (discoveryRepository.isInstalled) {
213+
if (discoveryRepositoryUi.isInstalled) {
214214
Spacer(Modifier.height(12.dp))
215215

216216
InstallStatusBadge(
217-
isUpdateAvailable = discoveryRepository.isUpdateAvailable,
217+
isUpdateAvailable = discoveryRepositoryUi.isUpdateAvailable,
218218
)
219219
}
220220

221-
if (discoveryRepository.repository.availablePlatforms.isNotEmpty()) {
221+
if (discoveryRepositoryUi.repository.availablePlatforms.isNotEmpty()) {
222222
Spacer(Modifier.height(12.dp))
223223

224224
FlowRow(
225225
horizontalArrangement = Arrangement.spacedBy(6.dp),
226226
verticalArrangement = Arrangement.spacedBy(6.dp),
227227
) {
228-
discoveryRepository.repository.availablePlatforms.forEach { platform ->
228+
discoveryRepositoryUi.repository.availablePlatforms.forEach { platform ->
229229
PlatformChip(platform = platform)
230230
}
231231
}
@@ -235,11 +235,11 @@ fun RepositoryCard(
235235

236236
val releasedAtText =
237237
buildAnnotatedString {
238-
if (hasWeekNotPassed(discoveryRepository.repository.updatedAt)) {
238+
if (hasWeekNotPassed(discoveryRepositoryUi.repository.updatedAt)) {
239239
append("🔥 ")
240240
}
241241

242-
append(formatReleasedAt(discoveryRepository.repository.updatedAt))
242+
append(formatReleasedAt(discoveryRepositoryUi.repository.updatedAt))
243243
}
244244

245245
Text(
@@ -281,7 +281,7 @@ fun RepositoryCard(
281281

282282
IconButton(
283283
onClick = {
284-
uriHandler.openUri(discoveryRepository.repository.htmlUrl)
284+
uriHandler.openUri(discoveryRepositoryUi.repository.htmlUrl)
285285
},
286286
colors =
287287
IconButtonDefaults.iconButtonColors(
@@ -413,15 +413,15 @@ fun InstallStatusBadge(
413413
fun RepositoryCardPreview() {
414414
GithubStoreTheme {
415415
RepositoryCard(
416-
discoveryRepository =
417-
DiscoveryRepository(
416+
discoveryRepositoryUi =
417+
DiscoveryRepositoryUi(
418418
repository =
419-
GithubRepoSummary(
419+
GithubRepoSummaryUi(
420420
id = 0L,
421421
name = "Hello",
422422
fullName = "JIFEOJEF",
423423
owner =
424-
GithubUser(
424+
GithubUserUi(
425425
id = 0L,
426426
login = "Skydoves",
427427
avatarUrl = "ewfew",

core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepository.kt renamed to core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepositoryUi.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package zed.rainxch.core.presentation.model
22

3-
import zed.rainxch.core.domain.model.GithubRepoSummary
4-
5-
data class DiscoveryRepository(
3+
data class DiscoveryRepositoryUi(
64
val isInstalled: Boolean,
75
val isUpdateAvailable: Boolean,
86
val isFavourite: Boolean,
97
val isStarred: Boolean,
10-
val repository: GithubRepoSummary,
8+
val repository: GithubRepoSummaryUi,
119
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package zed.rainxch.core.presentation.model
2+
3+
import kotlinx.collections.immutable.ImmutableList
4+
import kotlinx.collections.immutable.persistentListOf
5+
6+
data class GithubRepoSummaryUi(
7+
val id: Long,
8+
val name: String,
9+
val fullName: String,
10+
val owner: GithubUserUi,
11+
val description: String?,
12+
val defaultBranch: String,
13+
val htmlUrl: String,
14+
val stargazersCount: Int,
15+
val forksCount: Int,
16+
val language: String?,
17+
val topics: ImmutableList<String>?,
18+
val releasesUrl: String,
19+
val updatedAt: String,
20+
val isFork: Boolean = false,
21+
val availablePlatforms: ImmutableList<String> = persistentListOf(),
22+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package zed.rainxch.core.presentation.model
2+
3+
data class GithubUserUi(
4+
val id: Long,
5+
val login: String,
6+
val avatarUrl: String,
7+
val htmlUrl: String,
8+
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package zed.rainxch.core.presentation.utils
2+
3+
import kotlinx.collections.immutable.toImmutableList
4+
import zed.rainxch.core.domain.model.GithubRepoSummary
5+
import zed.rainxch.core.presentation.model.GithubRepoSummaryUi
6+
7+
fun GithubRepoSummary.toUi(): GithubRepoSummaryUi {
8+
return GithubRepoSummaryUi(
9+
id = id,
10+
name = name,
11+
fullName = fullName,
12+
owner = owner.toUi(),
13+
description = description,
14+
defaultBranch = defaultBranch,
15+
htmlUrl = htmlUrl,
16+
stargazersCount = stargazersCount,
17+
forksCount = forksCount,
18+
language = language,
19+
topics = topics?.toImmutableList(),
20+
releasesUrl = releasesUrl,
21+
updatedAt = updatedAt,
22+
isFork = isFork,
23+
availablePlatforms = availablePlatforms.toImmutableList()
24+
)
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package zed.rainxch.core.presentation.utils
2+
3+
import zed.rainxch.core.domain.model.GithubUser
4+
import zed.rainxch.core.presentation.model.GithubUserUi
5+
6+
fun GithubUser.toUi(): GithubUserUi {
7+
return GithubUserUi(
8+
id = id,
9+
login = login,
10+
avatarUrl = avatarUrl,
11+
htmlUrl = htmlUrl
12+
)
13+
}

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeAction.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package zed.rainxch.home.presentation
22

3-
import zed.rainxch.core.domain.model.GithubRepoSummary
3+
import zed.rainxch.core.presentation.model.GithubRepoSummaryUi
44
import zed.rainxch.home.domain.model.HomeCategory
55

66
sealed interface HomeAction {
@@ -17,15 +17,15 @@ sealed interface HomeAction {
1717
data object OnAppsClick : HomeAction
1818

1919
data class OnShareClick(
20-
val repo: GithubRepoSummary,
20+
val repo: GithubRepoSummaryUi,
2121
) : HomeAction
2222

2323
data class SwitchCategory(
2424
val category: HomeCategory,
2525
) : HomeAction
2626

2727
data class OnRepositoryClick(
28-
val repo: GithubRepoSummary,
28+
val repo: GithubRepoSummaryUi,
2929
) : HomeAction
3030

3131
data class OnRepositoryDeveloperClick(

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fun HomeRoot(
7373
onNavigateToSettings: () -> Unit,
7474
onNavigateToSearch: () -> Unit,
7575
onNavigateToApps: () -> Unit,
76-
onNavigateToDetails: (GithubRepoSummary) -> Unit,
76+
onNavigateToDetails: (repoId: Long) -> Unit,
7777
onNavigateToDeveloperProfile: (username: String) -> Unit,
7878
viewModel: HomeViewModel = koinViewModel(),
7979
) {
@@ -116,7 +116,7 @@ fun HomeRoot(
116116
}
117117

118118
is HomeAction.OnRepositoryClick -> {
119-
onNavigateToDetails(action.repo)
119+
onNavigateToDetails(action.repo.id)
120120
}
121121

122122
is HomeAction.OnRepositoryDeveloperClick -> {
@@ -235,7 +235,7 @@ private fun MainState(
235235
contentType = { "repo" },
236236
) { discoveryRepository ->
237237
RepositoryCard(
238-
discoveryRepository = discoveryRepository,
238+
discoveryRepositoryUi = discoveryRepository,
239239
onClick = {
240240
onAction(HomeAction.OnRepositoryClick(discoveryRepository.repository))
241241
},

0 commit comments

Comments
 (0)