Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -55,6 +56,7 @@ import com.sayanthrock.githubrock.ui.screens.DownloadsHubScreen
import com.sayanthrock.githubrock.ui.screens.FeaturePreviewScreen
import com.sayanthrock.githubrock.ui.screens.GitHubSettingsScreen
import com.sayanthrock.githubrock.ui.screens.HomeScreen
import com.sayanthrock.githubrock.ui.screens.GitHubJuiceScreen
import com.sayanthrock.githubrock.ui.screens.NativeProfileScreen
import com.sayanthrock.githubrock.ui.screens.ProfileScreen
import com.sayanthrock.githubrock.ui.screens.RepositoriesScreen
Expand All @@ -71,6 +73,7 @@ sealed class TopDestination(
data object Builds : TopDestination("builds", "Builds", Icons.Default.Build)
data object Downloads : TopDestination("downloads", "Downloads", Icons.Default.Download)
data object Profile : TopDestination("profile", "Profile", Icons.Default.AccountCircle)
data object Juice : TopDestination("juice", "Juice", Icons.Default.Star)
}

private const val FEATURES_PREVIEW_ROUTE = "features-preview"
Expand All @@ -97,7 +100,8 @@ private val topDestinations = listOf(
TopDestination.Repositories,
TopDestination.Builds,
TopDestination.Downloads,
TopDestination.Profile
TopDestination.Profile,
TopDestination.Juice
)

internal enum class MainNavigationLayout { BottomBar, NavigationRail }
Expand Down Expand Up @@ -188,6 +192,7 @@ fun MainNavigation(
BuildsScreen(mode, state.repositories, state.workflowRuns, openRepo)
}
composable(TopDestination.Downloads.route) { DownloadsHubScreen() }
composable(TopDestination.Juice.route) { GitHubJuiceScreen() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The Juice destination always instantiates GitHubJuiceScreen with an authenticated API-backed ViewModel, even when mode is Guest or Demo. Opening this route in those modes calls /user, /user/repos, and /user/starred, causing authorization failures and preventing the dashboard from displaying the mode's available data. Pass the current mode/data into the screen or provide a guest/demo-specific data path. [api mismatch]

Severity Level: Major ⚠️
- ❌ Juice dashboard fails in Guest and Demo modes.
- ⚠️ Demo mode violates its isolated-data contract.
- ⚠️ Account requests are made without active-mode handling.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** app/src/main/java/com/sayanthrock/githubrock/ui/navigation/AppNavigation.kt
**Line:** 195:195
**Comment:**
	*Api Mismatch: The Juice destination always instantiates `GitHubJuiceScreen` with an authenticated API-backed ViewModel, even when `mode` is `Guest` or `Demo`. Opening this route in those modes calls `/user`, `/user/repos`, and `/user/starred`, causing authorization failures and preventing the dashboard from displaying the mode's available data. Pass the current mode/data into the screen or provide a guest/demo-specific data path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

composable(TopDestination.Profile.route) {
ProfileScreen(
mode = mode,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
package com.sayanthrock.githubrock.ui.screens

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.sayanthrock.githubrock.core.model.GitHubRepositoryModel

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GitHubJuiceScreen(
viewModel: GitHubJuiceViewModel = hiltViewModel()
) {
val state by viewModel.state.collectAsState()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The screen collects state but never reads state.isLoading or state.error. If any one of the required requests fails, the ViewModel sets an error while the screen continues displaying the initial loading placeholders and provides no error message or retry action. Render an error state and expose a retry path. [incomplete implementation]

Severity Level: Major ⚠️
- ❌ Network failures leave users with misleading placeholders.
- ⚠️ Juice screen provides no visible retry path.
- ⚠️ GitHub API errors are hidden from users.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt
**Line:** 43:43
**Comment:**
	*Incomplete Implementation: The screen collects `state` but never reads `state.isLoading` or `state.error`. If any one of the required requests fails, the ViewModel sets an error while the screen continues displaying the initial loading placeholders and provides no error message or retry action. Render an error state and expose a retry path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎


Scaffold(
topBar = {
TopAppBar(
title = { Text("GitHub Juice") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
)
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
JuiceOverviewSection(
dailySummary = state.dailySummary,
healthScore = state.repositoryHealthScore,
commitActivity = state.commitActivity
)
}
item {
JuiceStatusSection(
openIssues = state.openIssuesSummary,
pullRequests = state.pullRequestStatus,
workflowStatus = state.workflowStatus,
recentReleases = state.recentReleases
)
}
item {
JuiceTrendingSection(
trendingRepos = state.trendingRepositories,
trendingDevs = state.trendingDevelopers
)
}
item {
JuiceGrowthSection(
repoGrowth = state.repositoryGrowth,
starGrowth = state.starGrowth,
forkGrowth = state.forkGrowth
)
}
item {
JuiceContributorsSection(
topContributors = state.topContributors,
recentContributors = state.recentContributors,
commitStreak = state.commitStreak
)
}
item {
JuiceCodeStatsSection(
languageBreakdown = state.languageBreakdown,
repoSize = state.repositorySize,
license = state.licenseDetection,
readmeStatus = state.readmeStatus,
latestTags = state.latestTags,
securityAdvisories = state.securityAdvisories,
codeFreq = state.codeFrequency,
timeline = state.activityTimeline
)
}
item {
JuiceListsSection(
leaderboard = state.contributorLeaderboard,
updatedRepos = state.recentlyUpdatedRepositories,
starredRepos = state.recentlyStarredRepositories,
savedRepos = state.savedRepositories
)
}
}
}
Comment on lines +43 to +118

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Surface state.isLoading and state.error in the UI.

GitHubJuiceState exposes isLoading and error, but this screen never reads either field. While isLoading is true, the user only sees the hardcoded "Loading..." placeholder strings baked into the default state — no progress indicator. If loadJuiceData() fails, error is set but never rendered, so the user has no feedback and no way to retry; the screen silently shows stale placeholder text forever.

🐛 Proposed fix (loading indicator + error banner)
     ) { paddingValues ->
+        if (state.isLoading) {
+            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                CircularProgressIndicator()
+            }
+            return@Scaffold
+        }
+        state.error?.let { message ->
+            Box(Modifier.fillMaxSize().padding(16.dp)) {
+                Text(text = message, color = MaterialTheme.colorScheme.error)
+            }
+        }
         LazyColumn(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val state by viewModel.state.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text("GitHub Juice") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
)
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
JuiceOverviewSection(
dailySummary = state.dailySummary,
healthScore = state.repositoryHealthScore,
commitActivity = state.commitActivity
)
}
item {
JuiceStatusSection(
openIssues = state.openIssuesSummary,
pullRequests = state.pullRequestStatus,
workflowStatus = state.workflowStatus,
recentReleases = state.recentReleases
)
}
item {
JuiceTrendingSection(
trendingRepos = state.trendingRepositories,
trendingDevs = state.trendingDevelopers
)
}
item {
JuiceGrowthSection(
repoGrowth = state.repositoryGrowth,
starGrowth = state.starGrowth,
forkGrowth = state.forkGrowth
)
}
item {
JuiceContributorsSection(
topContributors = state.topContributors,
recentContributors = state.recentContributors,
commitStreak = state.commitStreak
)
}
item {
JuiceCodeStatsSection(
languageBreakdown = state.languageBreakdown,
repoSize = state.repositorySize,
license = state.licenseDetection,
readmeStatus = state.readmeStatus,
latestTags = state.latestTags,
securityAdvisories = state.securityAdvisories,
codeFreq = state.codeFrequency,
timeline = state.activityTimeline
)
}
item {
JuiceListsSection(
leaderboard = state.contributorLeaderboard,
updatedRepos = state.recentlyUpdatedRepositories,
starredRepos = state.recentlyStarredRepositories,
savedRepos = state.savedRepositories
)
}
}
}
val state by viewModel.state.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text("GitHub Juice") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
)
)
}
) { paddingValues ->
if (state.isLoading) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
return@Scaffold
}
state.error?.let { message ->
Box(Modifier.fillMaxSize().padding(16.dp)) {
Text(text = message, color = MaterialTheme.colorScheme.error)
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
JuiceOverviewSection(
dailySummary = state.dailySummary,
healthScore = state.repositoryHealthScore,
commitActivity = state.commitActivity
)
}
item {
JuiceStatusSection(
openIssues = state.openIssuesSummary,
pullRequests = state.pullRequestStatus,
workflowStatus = state.workflowStatus,
recentReleases = state.recentReleases
)
}
item {
JuiceTrendingSection(
trendingRepos = state.trendingRepositories,
trendingDevs = state.trendingDevelopers
)
}
item {
JuiceGrowthSection(
repoGrowth = state.repositoryGrowth,
starGrowth = state.starGrowth,
forkGrowth = state.forkGrowth
)
}
item {
JuiceContributorsSection(
topContributors = state.topContributors,
recentContributors = state.recentContributors,
commitStreak = state.commitStreak
)
}
item {
JuiceCodeStatsSection(
languageBreakdown = state.languageBreakdown,
repoSize = state.repositorySize,
license = state.licenseDetection,
readmeStatus = state.readmeStatus,
latestTags = state.latestTags,
securityAdvisories = state.securityAdvisories,
codeFreq = state.codeFrequency,
timeline = state.activityTimeline
)
}
item {
JuiceListsSection(
leaderboard = state.contributorLeaderboard,
updatedRepos = state.recentlyUpdatedRepositories,
starredRepos = state.recentlyStarredRepositories,
savedRepos = state.savedRepositories
)
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt`
around lines 43 - 118, Update the GitHubJuiceScreen composable to consume
GitHubJuiceState.isLoading and error. Show a visible progress indicator while
isLoading is true, render the error message when error is present with a retry
action wired to the existing loadJuiceData mechanism, and keep the data sections
available for the normal loaded state.

}

@Composable
fun JuiceOverviewSection(dailySummary: String, healthScore: Int, commitActivity: String) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Overview", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Info, contentDescription = null, modifier = Modifier.size(20.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Daily Summary: $dailySummary", style = MaterialTheme.typography.bodyMedium)
}
Spacer(modifier = Modifier.height(4.dp))
Text(text = "Health Score: $healthScore / 100", style = MaterialTheme.typography.bodyMedium)
Text(text = "Commit Activity: $commitActivity", style = MaterialTheme.typography.bodyMedium)
}
}
}

@Composable
fun JuiceStatusSection(openIssues: String, pullRequests: String, workflowStatus: String, recentReleases: List<String>) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Status", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Open Issues: $openIssues", style = MaterialTheme.typography.bodyMedium)
Text(text = "Pull Requests: $pullRequests", style = MaterialTheme.typography.bodyMedium)
Text(text = "Workflows: $workflowStatus", style = MaterialTheme.typography.bodyMedium)
Text(text = "Recent Releases: ${recentReleases.size}", style = MaterialTheme.typography.bodyMedium)
}
}
}

@Composable
fun JuiceTrendingSection(trendingRepos: List<GitHubRepositoryModel>, trendingDevs: List<String>) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Trending", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Trending Repositories:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (trendingRepos.isEmpty()) {
Text(text = "No trending repositories found.", style = MaterialTheme.typography.bodySmall)
} else {
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
items(trendingRepos) { repo ->
ElevatedCard(modifier = Modifier.width(160.dp).padding(4.dp)) {
Column(modifier = Modifier.padding(8.dp)) {
Text(text = repo.name, style = MaterialTheme.typography.titleSmall, maxLines = 1)
Text(text = "⭐ ${repo.stars}", style = MaterialTheme.typography.bodySmall)
}
}
}
}
}
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Trending Developers:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
Text(text = if (trendingDevs.isEmpty()) "None currently" else trendingDevs.joinToString(", "), style = MaterialTheme.typography.bodySmall)
}
}
}

@Composable
fun JuiceGrowthSection(repoGrowth: String, starGrowth: String, forkGrowth: String) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Growth", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Repository Growth: $repoGrowth", style = MaterialTheme.typography.bodyMedium)
Text(text = "Star Growth: $starGrowth", style = MaterialTheme.typography.bodyMedium)
Text(text = "Fork Growth: $forkGrowth", style = MaterialTheme.typography.bodyMedium)
}
}
}

@Composable
fun JuiceContributorsSection(topContributors: List<String>, recentContributors: List<String>, commitStreak: Int) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Contributors", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Commit Streak: $commitStreak days", style = MaterialTheme.typography.bodyMedium)
Spacer(modifier = Modifier.height(4.dp))
Text(text = "Top Contributors:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
Text(text = if (topContributors.isEmpty()) "None currently" else topContributors.joinToString(", "), style = MaterialTheme.typography.bodySmall)
Spacer(modifier = Modifier.height(4.dp))
Text(text = "Recent Contributors:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
Text(text = if (recentContributors.isEmpty()) "None currently" else recentContributors.joinToString(", "), style = MaterialTheme.typography.bodySmall)
}
}
}

@Composable
fun JuiceCodeStatsSection(
languageBreakdown: Map<String, Double>,
repoSize: String,
license: String,
readmeStatus: String,
latestTags: List<String>,
securityAdvisories: List<String>,
codeFreq: String,
timeline: List<String>
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Code & Repository Stats", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))

Text(text = "Languages:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (languageBreakdown.isEmpty()) {
Text(text = "No language data", style = MaterialTheme.typography.bodySmall)
} else {
Text(text = languageBreakdown.entries.joinToString(", ") { "${it.key}: ${it.value}%" }, style = MaterialTheme.typography.bodySmall)
}
Comment on lines +227 to +232

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Format the language-breakdown percentage before display.

"${it.key}: ${it.value}%" prints the raw Double, which can render with many decimal places (for example Kotlin: 45.83333333333333%). Round to one decimal place for readability.

💚 Proposed fix
-                Text(text = languageBreakdown.entries.joinToString(", ") { "${it.key}: ${it.value}%" }, style = MaterialTheme.typography.bodySmall)
+                Text(
+                    text = languageBreakdown.entries.joinToString(", ") { "${it.key}: ${"%.1f".format(it.value)}%" },
+                    style = MaterialTheme.typography.bodySmall
+                )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Text(text = "Languages:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (languageBreakdown.isEmpty()) {
Text(text = "No language data", style = MaterialTheme.typography.bodySmall)
} else {
Text(text = languageBreakdown.entries.joinToString(", ") { "${it.key}: ${it.value}%" }, style = MaterialTheme.typography.bodySmall)
}
Text(text = "Languages:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (languageBreakdown.isEmpty()) {
Text(text = "No language data", style = MaterialTheme.typography.bodySmall)
} else {
Text(
text = languageBreakdown.entries.joinToString(", ") { "${it.key}: ${"%.1f".format(it.value)}%" },
style = MaterialTheme.typography.bodySmall
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt`
around lines 227 - 232, Update the languageBreakdown display in
GitHubJuiceScreen so each it.value percentage is rounded and formatted to one
decimal place before appending the percent sign, while preserving the existing
language name and comma-separated output.

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

Text(text = "Size: $repoSize", style = MaterialTheme.typography.bodyMedium)
Text(text = "License: $license", style = MaterialTheme.typography.bodyMedium)
Text(text = "README: $readmeStatus", style = MaterialTheme.typography.bodyMedium)
Text(text = "Code Frequency: $codeFreq", style = MaterialTheme.typography.bodyMedium)

Spacer(modifier = Modifier.height(4.dp))
Text(text = "Latest Tags:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
Text(text = if (latestTags.isEmpty()) "None" else latestTags.joinToString(", "), style = MaterialTheme.typography.bodySmall)

Spacer(modifier = Modifier.height(4.dp))
Text(text = "Security Advisories:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.error)
Text(text = if (securityAdvisories.isEmpty()) "None found" else securityAdvisories.joinToString(", "), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.error)

Spacer(modifier = Modifier.height(4.dp))
Text(text = "Activity Timeline:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (timeline.isEmpty()) {
Text(text = "No recent activity.", style = MaterialTheme.typography.bodySmall)
} else {
timeline.forEach { event ->
Text(text = "- $event", style = MaterialTheme.typography.bodySmall)
}
}
}
}
}

@Composable
fun JuiceListsSection(
leaderboard: List<String>,
updatedRepos: List<GitHubRepositoryModel>,
starredRepos: List<GitHubRepositoryModel>,
savedRepos: List<GitHubRepositoryModel>
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Lists & Actions", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))

Text(text = "Contributor Leaderboard:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
Text(text = if (leaderboard.isEmpty()) "None currently" else leaderboard.joinToString(", "), style = MaterialTheme.typography.bodySmall)

Spacer(modifier = Modifier.height(16.dp))
Text(text = "Recently Updated Repositories:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (updatedRepos.isEmpty()) {
Text(text = "No recent updates.", style = MaterialTheme.typography.bodySmall)
} else {
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
items(updatedRepos) { repo ->
RepositoryCardWithActions(repo)
}
}
}

Spacer(modifier = Modifier.height(16.dp))
Text(text = "Recently Starred Repositories:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (starredRepos.isEmpty()) {
Text(text = "No recent stars.", style = MaterialTheme.typography.bodySmall)
} else {
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
items(starredRepos) { repo ->
RepositoryCardWithActions(repo)
}
}
}

Spacer(modifier = Modifier.height(16.dp))
Text(text = "Saved Repositories:", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
if (savedRepos.isEmpty()) {
Text(text = "No saved repositories.", style = MaterialTheme.typography.bodySmall)
} else {
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
items(savedRepos) { repo ->
RepositoryCardWithActions(repo)
}
}
}
}
}
}

@Composable
fun RepositoryCardWithActions(repo: GitHubRepositoryModel) {
ElevatedCard(modifier = Modifier.width(240.dp).padding(4.dp)) {
Column(modifier = Modifier.padding(12.dp)) {
Text(text = repo.name, style = MaterialTheme.typography.titleSmall, maxLines = 1)
Text(text = repo.description ?: "No description", style = MaterialTheme.typography.bodySmall, maxLines = 2, modifier = Modifier.height(40.dp))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid a fixed height combined with maxLines for wrapped description text.

Modifier.height(40.dp) combined with maxLines = 2 can clip the description when the user increases the system font scale, because the fixed height no longer matches two lines of larger text. Rely on maxLines alone, or use heightIn(min = ...) instead of a fixed height.

💚 Proposed fix
-            Text(text = repo.description ?: "No description", style = MaterialTheme.typography.bodySmall, maxLines = 2, modifier = Modifier.height(40.dp))
+            Text(text = repo.description ?: "No description", style = MaterialTheme.typography.bodySmall, maxLines = 2)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Text(text = repo.description ?: "No description", style = MaterialTheme.typography.bodySmall, maxLines = 2, modifier = Modifier.height(40.dp))
Text(text = repo.description ?: "No description", style = MaterialTheme.typography.bodySmall, maxLines = 2)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt`
at line 320, Update the repository description Text in GitHubJuiceScreen to
remove the fixed Modifier.height(40.dp), keeping maxLines = 2 so wrapped text
can expand appropriately with increased font scaling; use heightIn only if a
minimum height is required.

Spacer(modifier = Modifier.height(8.dp))
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
TextButton(onClick = { /* Handle Star */ }, contentPadding = PaddingValues(4.dp)) {
Text("Star", style = MaterialTheme.typography.labelSmall)
}
Comment on lines +323 to +325

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: All repository action buttons have empty callbacks, so tapping Star, Watch, Fork, Clone, or Browser produces no operation or navigation despite presenting them as functional actions. Wire these callbacks to the corresponding ViewModel/API and browser/navigation handlers, or remove the buttons until implemented. [incomplete implementation]

Severity Level: Major ⚠️
- ❌ Star action does not update GitHub.
- ❌ Fork action does not create forks.
- ⚠️ Browser and Clone actions provide no navigation.
- ⚠️ Visible controls falsely imply available operations.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt
**Line:** 323:325
**Comment:**
	*Incomplete Implementation: All repository action buttons have empty callbacks, so tapping Star, Watch, Fork, Clone, or Browser produces no operation or navigation despite presenting them as functional actions. Wire these callbacks to the corresponding ViewModel/API and browser/navigation handlers, or remove the buttons until implemented.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

TextButton(onClick = { /* Handle Watch */ }, contentPadding = PaddingValues(4.dp)) {
Text("Watch", style = MaterialTheme.typography.labelSmall)
}
TextButton(onClick = { /* Handle Fork */ }, contentPadding = PaddingValues(4.dp)) {
Text("Fork", style = MaterialTheme.typography.labelSmall)
}
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
TextButton(onClick = { /* Handle Clone */ }, contentPadding = PaddingValues(4.dp)) {
Text("Clone", style = MaterialTheme.typography.labelSmall)
}
TextButton(onClick = { /* Handle Open */ }, contentPadding = PaddingValues(4.dp)) {
Text("Browser", style = MaterialTheme.typography.labelSmall)
}
}
}
}
}
Loading