Skip to content

✨ GitHub Juice section - #217

Merged
SayanthRock merged 1 commit into
mainfrom
feature/github-juice-insights-17229685807243298245
Aug 6, 2026
Merged

✨ GitHub Juice section#217
SayanthRock merged 1 commit into
mainfrom
feature/github-juice-insights-17229685807243298245

Conversation

@SayanthRock

@SayanthRock SayanthRock commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

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 GitHubJuiceRepository that 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 in cacheDir for lightning-fast initial load times.

The GitHubJuiceScreen UI has been updated to enforce strict Material3 colors and utilizes animateContentSize on 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

  • GitHub Juice now shows trending developers, top and recent contributors, commit streaks, repository sizes, licenses, README coverage, tags, and security alerts
  • Insights load from a local snapshot first, so previously viewed data appears while fresh GitHub data is retrieved
  • Repository cards now let users copy a clone URL and open the repository in a browser
  • Dashboard cards animate when their content changes and use clearer theme-aware text colors

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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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

    • Added richer repository insights, including contribution streaks, size, licenses, tags, README coverage, and security alerts.
    • Added contributor and user search data support.
    • Repository cards now let you copy clone URLs and open repositories in a browser.
  • Improvements

    • Dashboard content updates now animate smoothly.
    • Cached dashboard data appears first while refreshed information loads.
    • Improved handling of unavailable GitHub data with graceful fallback results.

- 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>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI lite review requested due to automatic review settings August 5, 2026 20:12
@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 647c4ad Aug 05, 2026 · 20:12 20:15

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@ai-coding-guardrails

Copy link
Copy Markdown

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

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

GitHub Juice dashboard

Layer / File(s) Summary
Contributor contracts and REST endpoints
app/src/main/java/com/sayanthrock/githubrock/core/model/ContributorModels.kt, app/src/main/java/com/sayanthrock/githubrock/core/network/GitHubApi.kt
Adds serializable contributor and user-search models. Adds paginated user-search and repository-contributor endpoints.
Cached REST dashboard assembly
app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt
Adds serialized cache access, concurrent GitHub data collection, REST failure handling, metric calculations, health scoring, language percentages, and dashboard state construction.
GraphQL metrics parsing
app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt
Parses contribution streaks, repository size, licenses, tags, README status, and vulnerability alerts into GraphQlStats, with fallback values for errors.
ViewModel cache-first loading
app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceViewModel.kt
Replaces direct API usage with GitHubJuiceRepository. The ViewModel restores cached state, fetches refreshed state, saves it, and emits it.
Dashboard presentation and repository actions
app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt
Adds content-size animations, explicit theme colors, clone URL clipboard copying, and browser navigation for repository URLs. Star, Watch, and Fork remain placeholders.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: zenable/risk:medium

Suggested reviewers: copilot

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
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the main change, which is the addition of the GitHub Juice dashboard section.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/github-juice-insights-17229685807243298245

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Keep the load guard active while the request starts.

loadJuiceData() is public and hasLoaded is updated only after the request and cache write complete. Call repository.fetchJuiceState() concurrently on a fast second invocation and start two REST and GraphQL loads. Store the active Job or set a loading guard before viewModelScope.launch and cancel/clear it from the catch block.

🤖 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 value

Import the new model types instead of fully qualifying them.

Every other method in GitHubRestApi returns an imported simple type name, for example RepositorySearchResponse at 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.model imports:

+import com.sayanthrock.githubrock.core.model.Contributor
+import com.sayanthrock.githubrock.core.model.UserSearchResponse

Then 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 value

Remove 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 use readme.md, README.rst, or README are 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 win

Avoid a duplicate contributors request and run the two fetches concurrently.

topRepo and recentRepo are frequently the same repository. When they match, the code issues two identical contributors requests. 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 mutableListOf declarations 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 value

Write the cache atomically.

writeText truncates 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. getCachedState then 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba8316 and 647c4ad.

⛔ Files ignored due to path filters (2)
  • .kotlin/errors/errors-1785959363357.log is excluded by !**/*.log
  • .kotlin/errors/errors-1785959459930.log is excluded by !**/*.log
📒 Files selected for processing (5)
  • app/src/main/java/com/sayanthrock/githubrock/core/model/ContributorModels.kt
  • app/src/main/java/com/sayanthrock/githubrock/core/network/GitHubApi.kt
  • app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt
  • app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceScreen.kt
  • app/src/main/java/com/sayanthrock/githubrock/ui/screens/GitHubJuiceViewModel.kt

Comment thread app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt Dismissed
Comment thread app/src/main/java/com/sayanthrock/githubrock/data/repository/GitHubJuiceRepository.kt Dismissed
@SayanthRock
SayanthRock merged commit f18fcf8 into main Aug 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants