-
-
Notifications
You must be signed in to change notification settings - Fork 442
feat: add download count and license info to repository details #426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5829fa6
72f386d
649da36
4f8b676
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,8 +21,11 @@ import androidx.compose.material.icons.filled.CheckCircle | |||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.Favorite | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.OpenInBrowser | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.Share | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.Star | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.Update | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.outlined.Code | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.outlined.Download | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.filled.Star | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.outlined.StarOutline | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material.icons.outlined.Visibility | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi | ||||||||||||||||||||||||||||||||||||||||
| import androidx.compose.material3.Icon | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -188,37 +191,32 @@ fun RepositoryCard( | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Spacer(Modifier.height(8.dp)) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||||||||||||||||||
| FlowRow( | ||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.fillMaxWidth(), | ||||||||||||||||||||||||||||||||||||||||
| verticalAlignment = Alignment.CenterVertically, | ||||||||||||||||||||||||||||||||||||||||
| horizontalArrangement = Arrangement.spacedBy(16.dp), | ||||||||||||||||||||||||||||||||||||||||
| horizontalArrangement = Arrangement.spacedBy(8.dp), | ||||||||||||||||||||||||||||||||||||||||
| verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically), | ||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||
| text = "⭐ ${discoveryRepositoryUi.repository.stargazersCount}", | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.titleMedium, | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| maxLines = 1, | ||||||||||||||||||||||||||||||||||||||||
| softWrap = false, | ||||||||||||||||||||||||||||||||||||||||
| overflow = TextOverflow.Ellipsis, | ||||||||||||||||||||||||||||||||||||||||
| InfoChip( | ||||||||||||||||||||||||||||||||||||||||
| icon = Icons.Outlined.StarOutline, | ||||||||||||||||||||||||||||||||||||||||
| text = formatCount(discoveryRepositoryUi.repository.stargazersCount.toLong()), | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||
| text = "• 🌴 ${discoveryRepositoryUi.repository.forksCount}", | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.titleMedium, | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| maxLines = 1, | ||||||||||||||||||||||||||||||||||||||||
| softWrap = false, | ||||||||||||||||||||||||||||||||||||||||
| overflow = TextOverflow.Ellipsis, | ||||||||||||||||||||||||||||||||||||||||
| InfoChip( | ||||||||||||||||||||||||||||||||||||||||
| icon = Icons.AutoMirrored.Outlined.CallSplit, | ||||||||||||||||||||||||||||||||||||||||
| text = formatCount(discoveryRepositoryUi.repository.forksCount.toLong()), | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (discoveryRepositoryUi.repository.downloadCount > 0) { | ||||||||||||||||||||||||||||||||||||||||
| InfoChip( | ||||||||||||||||||||||||||||||||||||||||
| icon = Icons.Outlined.Download, | ||||||||||||||||||||||||||||||||||||||||
| text = formatCount(discoveryRepositoryUi.repository.downloadCount), | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| discoveryRepositoryUi.repository.language?.let { | ||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||
| text = "• $it", | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.titleMedium, | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| maxLines = 1, | ||||||||||||||||||||||||||||||||||||||||
| softWrap = false, | ||||||||||||||||||||||||||||||||||||||||
| overflow = TextOverflow.Ellipsis, | ||||||||||||||||||||||||||||||||||||||||
| InfoChip( | ||||||||||||||||||||||||||||||||||||||||
| icon = Icons.Outlined.Code, | ||||||||||||||||||||||||||||||||||||||||
| text = it, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -351,7 +349,7 @@ fun PlatformChip( | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||
| text = platform.name, | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.labelSmall, | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.bodySmall, | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| fontWeight = FontWeight.Medium, | ||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp), | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -515,3 +513,43 @@ fun RepositoryCardPreview() { | |||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||||||||||||||||||
| private fun InfoChip( | ||||||||||||||||||||||||||||||||||||||||
| icon: androidx.compose.ui.graphics.vector.ImageVector, | ||||||||||||||||||||||||||||||||||||||||
| text: String, | ||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||
| Surface( | ||||||||||||||||||||||||||||||||||||||||
| shape = RoundedCornerShape(8.dp), | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.surfaceContainerHigh, | ||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), | ||||||||||||||||||||||||||||||||||||||||
| verticalAlignment = Alignment.CenterVertically, | ||||||||||||||||||||||||||||||||||||||||
| horizontalArrangement = Arrangement.spacedBy(4.dp), | ||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||
| Icon( | ||||||||||||||||||||||||||||||||||||||||
| imageVector = icon, | ||||||||||||||||||||||||||||||||||||||||
| contentDescription = null, | ||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.size(14.dp), | ||||||||||||||||||||||||||||||||||||||||
| tint = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||
| text = text, | ||||||||||||||||||||||||||||||||||||||||
| style = MaterialTheme.typography.labelMedium, | ||||||||||||||||||||||||||||||||||||||||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||||||||||||||||||||||||||||||||||||||
| fontWeight = FontWeight.Medium, | ||||||||||||||||||||||||||||||||||||||||
| maxLines = 1, | ||||||||||||||||||||||||||||||||||||||||
| softWrap = false, | ||||||||||||||||||||||||||||||||||||||||
| overflow = TextOverflow.Ellipsis, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| private fun formatCount(count: Long): String = | ||||||||||||||||||||||||||||||||||||||||
| when { | ||||||||||||||||||||||||||||||||||||||||
| count >= 1_000_000 -> String.format("%.1fM", count / 1_000_000.0) | ||||||||||||||||||||||||||||||||||||||||
| count >= 1_000 -> String.format("%.1fK", count / 1_000.0) | ||||||||||||||||||||||||||||||||||||||||
| else -> count.toString() | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+550
to
+555
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No, String.format is not available in the Kotlin commonMain source set. It is a JVM-specific extension function in the Kotlin standard library, implemented by forwarding to Java's String.format method. This is confirmed by official Kotlin documentation marking it as JVM-only, Stack Overflow reports of unresolved references in KMP commonMain (especially with wasmJS), and the absence of format functions in the common stdlib _Strings.kt source files. Instead, it appears only in JVM-specific files like StringsJVM.kt. There are no overloads of String.format in commonMain because the function itself is unavailable there. For multiplatform code, alternatives include string templates, platform-specific expect/actual implementations, or third-party libraries like mp_stools that provide sprintf-like functionality across platforms. Citations:
String.format is JVM-only and unavailable in commonMain; code will not compile for non-JVM targets. This function is in 🛠 Proposed fixReplace String.format with locale-independent string building: -private fun formatCount(count: Long): String =
- when {
- count >= 1_000_000 -> String.format("%.1fM", count / 1_000_000.0)
- count >= 1_000 -> String.format("%.1fK", count / 1_000.0)
- else -> count.toString()
- }
+private fun formatCount(count: Long): String {
+ fun trimmed(value: Double, suffix: String): String {
+ val tenths = (value * 10).toLong()
+ val whole = tenths / 10
+ val frac = tenths % 10
+ return if (frac == 0L) "$whole$suffix" else "$whole.$frac$suffix"
+ }
+ return when {
+ count >= 1_000_000 -> trimmed(count / 1_000_000.0, "M")
+ count >= 1_000 -> trimmed(count / 1_000.0, "K")
+ else -> count.toString()
+ }
+}This avoids JVM-only APIs and removes locale dependency. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 detekt (1.23.8)[warning] 552-552: String.format("%.1fM", count / 1_000_000.0) uses implicitly default locale for string formatting. (detekt.potential-bugs.ImplicitDefaultLocale) [warning] 553-553: String.format("%.1fK", count / 1_000.0) uses implicitly default locale for string formatting. (detekt.potential-bugs.ImplicitDefaultLocale) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor:
downloadCountsilently stays0on the GitHub-API fallback path.Per the relevant snippet from
core/data/src/commonMain/kotlin/zed/rainxch/core/data/mappers/GithubRepoMapper.kt,GithubRepoNetworkModel.toSummary()does not setdownloadCount, so whenDetailsRepositoryImplfalls back from the backend to the GitHub API, the downloads chip will be hidden (due to thedownloadCount > 0guard inRepositoryCard) rather than showing real data. This is likely intentional since GitHub's repo endpoint doesn't expose aggregate downloads — just worth confirming the product expectation for the fallback path (silent omission vs. summing asset download counts when releases are already loaded).🤖 Prompt for AI Agents