✨ GitHub Juice section - #217
Conversation
- Created local cached GitHubJuiceRepository for robust async fetching. - Added Trending Users and Top/Recent contributors functionality via the GitHub REST API. - Re-architected GitHubJuiceViewModel to orchestrate through the Repository instead of fetching raw endpoints. - Incorporated GitHub GraphQL API to fetch user Streak, complex Repository size/license metrics, and vulnerability alerts. - Added Material3 animations via animateContentSize, explicitly tied material tokens to typography, and fully hooked up the UI Quick Action buttons. Co-authored-by: SayanthRock <202829406+SayanthRock@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
|
You've hit your review limit for the week, but don't worry you'll get some more next week! Contact us at hello@zenable.io if you want this rate limit to go away |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughThe change adds contributor models and REST endpoints, centralizes GitHub Juice data loading and caching in a repository, adds GraphQL metric parsing, updates ViewModel state restoration, and adds dashboard animations plus repository clipboard and browser actions. ChangesGitHub Juice dashboard
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant GitHubJuiceScreen
participant GitHubJuiceViewModel
participant GitHubJuiceRepository
participant GitHubRestApi
participant GitHubGraphQlApi
GitHubJuiceScreen->>GitHubJuiceViewModel: request dashboard state
GitHubJuiceViewModel->>GitHubJuiceRepository: getCachedState()
GitHubJuiceRepository-->>GitHubJuiceViewModel: cached GitHubJuiceState
GitHubJuiceViewModel-->>GitHubJuiceScreen: emit cached state
GitHubJuiceViewModel->>GitHubJuiceRepository: fetchJuiceState()
GitHubJuiceRepository->>GitHubRestApi: fetch REST dashboard data
GitHubJuiceRepository->>GitHubGraphQlApi: fetch GraphQL metrics
GitHubRestApi-->>GitHubJuiceRepository: REST results
GitHubGraphQlApi-->>GitHubJuiceRepository: GraphQL results
GitHubJuiceRepository-->>GitHubJuiceViewModel: refreshed GitHubJuiceState
GitHubJuiceViewModel->>GitHubJuiceRepository: saveCachedState(state)
GitHubJuiceViewModel-->>GitHubJuiceScreen: emit refreshed state
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceViewModel.kt (1)
64-79: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winKeep the load guard active while the request starts.
loadJuiceData()is public andhasLoadedis updated only after the request and cache write complete. Callrepository.fetchJuiceState()concurrently on a fast second invocation and start two REST and GraphQL loads. Store the activeJobor set a loading guard beforeviewModelScope.launchand cancel/clear it from thecatchblock.🤖 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/GitHubJuiceViewModel.kt` around lines 64 - 79, The load guard in GitHubJuiceViewModel.loadJuiceData must prevent concurrent requests before launching work. Set an active-loading Job or equivalent guard before viewModelScope.launch, keep subsequent calls as no-ops while the request is active, and clear or cancel that guard in the existing catch path so retries remain possible after failure.
🧹 Nitpick comments (4)
app/src/main/java/com/sayanthrock/githubrock/core/network/GitHubApi.kt (1)
95-110: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueImport the new model types instead of fully qualifying them.
Every other method in
GitHubRestApireturns an imported simple type name, for exampleRepositorySearchResponseat line 93. These two methods use fully qualified names. Add imports to match the surrounding style.♻️ Proposed change
Add the imports near the other
core.modelimports:+import com.sayanthrock.githubrock.core.model.Contributor +import com.sayanthrock.githubrock.core.model.UserSearchResponseThen simplify the return types:
- ): com.sayanthrock.githubrock.core.model.UserSearchResponse + ): UserSearchResponse @@ - ): List<com.sayanthrock.githubrock.core.model.Contributor> + ): List<Contributor>🤖 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/core/network/GitHubApi.kt` around lines 95 - 110, Import UserSearchResponse and Contributor alongside the other core.model types in GitHubRestApi, then update searchUsers and contributors to use those simple type names instead of fully qualified references.app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt (3)
182-182: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueRemove the unused field and widen the README lookup.
Two points in the query:
- Line 182 requests
hasIssuesEnabled, but no code reads it. Remove it to reduce the query cost.- Line 191 matches only
README.md. Repositories that usereadme.md,README.rst, orREADMEare counted as missing a README, which understates the coverage percentage on line 257.Also applies to: 191-191
🤖 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/data/repository/GitHubJuiceRepository.kt` at line 182, In the repository query built by GitHubJuiceRepository, remove the unused hasIssuesEnabled field and broaden the README lookup beyond README.md to recognize case variants and supported extensions such as readme.md, README.rst, and README, preserving the existing coverage calculation.
87-103: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid a duplicate contributors request and run the two fetches concurrently.
topRepoandrecentRepoare frequently the same repository. When they match, the code issues two identicalcontributorsrequests. Both requests also run sequentially after all other awaits, which extends the total load time. Deduplicate the repositories first, then fetch concurrently.♻️ Proposed change
- if (repos.isNotEmpty()) { - val topRepo = repos.maxByOrNull { it.stars } - if (topRepo != null && !topRepo.fork) { - try { - val contribs = gitHubApi.contributors(owner = topRepo.owner.login, repo = topRepo.name) - topContributors.addAll(contribs.take(5).map { it.login }) - } catch (e: Exception) { } - } - - val recentRepo = repos.maxByOrNull { it.updatedAt } - if (recentRepo != null && !recentRepo.fork) { - try { - val contribs = gitHubApi.contributors(owner = recentRepo.owner.login, repo = recentRepo.name) - recentContributors.addAll(contribs.take(5).map { it.login }) - } catch (e: Exception) { } - } - } + suspend fun loginsFor(repo: GitHubRepositoryModel?): List<String> { + if (repo == null || repo.fork) return emptyList() + return try { + gitHubApi.contributors(owner = repo.owner.login, repo = repo.name) + .take(5) + .map { it.login } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + Log.w(TAG, "Contributors fetch failed for ${repo.name}", e) + emptyList() + } + } + + val topRepo = repos.maxByOrNull { it.stars } + val recentRepo = repos.maxByOrNull { it.updatedAt } + val topContributors: List<String> + val recentContributors: List<String> + if (topRepo != null && topRepo == recentRepo) { + topContributors = loginsFor(topRepo) + recentContributors = topContributors + } else { + val topDeferred = async { loginsFor(topRepo) } + val recentDeferred = async { loginsFor(recentRepo) } + topContributors = topDeferred.await() + recentContributors = recentDeferred.await() + }Remove the
mutableListOfdeclarations at lines 84-85 accordingly.🤖 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/data/repository/GitHubJuiceRepository.kt` around lines 87 - 103, Update the repository contributor-fetching block to deduplicate topRepo and recentRepo before requesting contributors, avoiding duplicate requests when they refer to the same repository. Run the remaining contributor requests concurrently using the existing coroutine context, preserve the top/recent contributor result assignments, and remove the now-unneeded mutableListOf declarations.
49-55: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low valueWrite the cache atomically.
writeTexttruncates the file before it writes the new content. If the process dies mid-write, or if a read runs concurrently, the cache file is left partially written.getCachedStatethen fails to decode and discards the cache. Write to a temporary file and rename it to make the replacement atomic.♻️ Proposed change
suspend fun saveCachedState(state: GitHubJuiceState) = withContext(Dispatchers.IO) { try { - cacheFile.writeText(json.encodeToString(state)) - } catch (e: Exception) { - // Ignore cache save errors + val tempFile = File(cacheFile.parentFile, "${cacheFile.name}.tmp") + tempFile.writeText(json.encodeToString(state)) + if (!tempFile.renameTo(cacheFile)) { + tempFile.delete() + } + } catch (e: Exception) { + Log.w(TAG, "Failed to save GitHub Juice cache", e) } }🤖 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/data/repository/GitHubJuiceRepository.kt` around lines 49 - 55, Update saveCachedState to serialize into a temporary file in the same directory, then atomically replace cacheFile via rename or move, preserving the existing IO context and ignored-error behavior. Ensure temporary files are cleaned up when the write or replacement fails, and keep getCachedState unchanged.
🤖 Prompt for all review comments with 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.
Inline comments:
In
`@app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt`:
- Line 64: Update the trendingReposDeferred flow alongside trendingDevsDeferred
to catch GitHub search failures, including rate-limit errors, and return an
empty list so the enclosing dashboard scope remains successful. Update the
corresponding consumer to use the deferred result as a list directly, matching
the proposed guarded implementation and preserving the existing fallback
behavior.
- Line 137: Update the pullRequestStatus assignment in the repository status
construction to match the value it displays: since it counts forked
repositories, replace the pull-request wording with an active-forks label, while
leaving the existing fork count calculation unchanged.
- Around line 258-264: Update the repo size formatting in the GraphQlStats
construction to use a consistent 1024 MB threshold and divisor, and format GB
values with one decimal place to avoid truncation such as 0 GB. Preserve the
existing MB output for smaller repositories and add the required formatting
import if needed.
- Around line 196-198: Update fetchGraphQlStats() to inspect
GraphQlResponse.errors for vulnerabilityAlerts failures before building
secAdvisories; when that query reports authentication or permission errors,
return a non-zero advisory result or the repository’s established explicit
read-error message instead of treating the partial totalCount as zero. Preserve
the existing totalCount behavior when no GraphQL errors are present.
- Around line 214-227: Update the current-streak scan in the repository method
around currentStreak so it ignores contribution days whose dates are after today
before evaluating the streak. Apply the zero-count exemption only when the day
is today, not based on its array index, then count backward through prior days
until the first zero. Replace the non-null assertion on contributionDays with
safe handling so a missing key is skipped or treated as empty without aborting
the metrics calculation.
- Around line 270-272: Six catch blocks in GitHubJuiceRepository swallow
failures and coroutine cancellation. At
app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt
lines 270-272, 70-72, 93-93, and 101-101, rethrow CancellationException, log
other exceptions, then preserve the existing GraphQlStats(), emptyList(), or
contributor fallback; at lines 44-46, rethrow cancellation and log cache decode
failures before returning null; at lines 52-54, replace the ignored cache-save
comment with a warning log.
- Around line 62-69: Update the trending query construction in
GitHubJuiceRepository so trendingQueries contain only search qualifiers, and
pass sorting through the searchRepositories and searchUsers sort/order
parameters. Preserve repository sorting by stars descending and developer
sorting by joined date descending.
In
`@app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt`:
- Around line 333-341: Update the Star, Watch, and Fork TextButton handlers in
GitHubJuiceScreen so they no longer expose enabled no-op actions: connect each
to the corresponding ViewModel action, or hide/disable the buttons until those
actions are implemented.
---
Outside diff comments:
In
`@app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceViewModel.kt`:
- Around line 64-79: The load guard in GitHubJuiceViewModel.loadJuiceData must
prevent concurrent requests before launching work. Set an active-loading Job or
equivalent guard before viewModelScope.launch, keep subsequent calls as no-ops
while the request is active, and clear or cancel that guard in the existing
catch path so retries remain possible after failure.
---
Nitpick comments:
In `@app/src/main/java/com/sayanthrock/githubrock/core/network/GitHubApi.kt`:
- Around line 95-110: Import UserSearchResponse and Contributor alongside the
other core.model types in GitHubRestApi, then update searchUsers and
contributors to use those simple type names instead of fully qualified
references.
In
`@app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt`:
- Line 182: In the repository query built by GitHubJuiceRepository, remove the
unused hasIssuesEnabled field and broaden the README lookup beyond README.md to
recognize case variants and supported extensions such as readme.md, README.rst,
and README, preserving the existing coverage calculation.
- Around line 87-103: Update the repository contributor-fetching block to
deduplicate topRepo and recentRepo before requesting contributors, avoiding
duplicate requests when they refer to the same repository. Run the remaining
contributor requests concurrently using the existing coroutine context, preserve
the top/recent contributor result assignments, and remove the now-unneeded
mutableListOf declarations.
- Around line 49-55: Update saveCachedState to serialize into a temporary file
in the same directory, then atomically replace cacheFile via rename or move,
preserving the existing IO context and ignored-error behavior. Ensure temporary
files are cleaned up when the write or replacement fails, and keep
getCachedState unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: dbe63e13-b353-4fa7-a060-a92992efccb0
⛔ Files ignored due to path filters (2)
.kotlin/errors/errors-1785959363357.logis excluded by!**/*.log.kotlin/errors/errors-1785959459930.logis excluded by!**/*.log
📒 Files selected for processing (5)
app/src/main/java/com/sayanthrock/githubrock/core/model/ContributorModels.ktapp/src/main/java/com/sayanthrock/githubrock/core/network/GitHubApi.ktapp/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.ktapp/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.ktapp/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceViewModel.kt
User description
Implemented a comprehensive "GitHub Juice" developer insights dashboard using exclusively free GitHub REST and GraphQL APIs.
The data aggregation relies on a newly structured
GitHubJuiceRepositorythat manages complex parallel requests via Coroutines, parsing details like commit streaks, repository sizes, vulnerability alerts, trending developers, and top contributors. It caches the latest JSON snapshot locally incacheDirfor lightning-fast initial load times.The
GitHubJuiceScreenUI has been updated to enforce strict Material3 colors and utilizesanimateContentSizeon cards. All quick action features (Browser/Clone) are wired correctly natively via Compose APIs.PR created automatically by Jules for task 17229685807243298245 started by @SayanthRock
CodeAnt-AI Description
Add GitHub Juice insights with cached data and repository activity details
What Changed
Impact
✅ Faster GitHub dashboard startup✅ Clearer repository health and security insights✅ Direct browser access and clone-link copying💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
Summary by CodeRabbit
New Features
Improvements