Skip to content

Improve pinned participant state on rejoin with new session#1683

Open
rahul-lohra wants to merge 22 commits into
developfrom
feature/rahullohra/pinned-users-2
Open

Improve pinned participant state on rejoin with new session#1683
rahul-lohra wants to merge 22 commits into
developfrom
feature/rahullohra/pinned-users-2

Conversation

@rahul-lohra
Copy link
Copy Markdown
Contributor

@rahul-lohra rahul-lohra commented May 15, 2026

Goal

Server-side pin synchronization was previously tightly coupled to sessionIds only. This created issues during participant reconnection flows where a participant could reconnect with a new sessionId while still representing the same user.

The goal of this change is to make server pin resolution resilient to reconnect scenarios while preserving the server-defined pin priority order.

Additionally:

  • Improve determinism and testability of timestamp handling by introducing TimeProvider
  • Preserve stable pin ordering across updates
  • Avoid ambiguous reconnect matching when multiple pins share the same userId
  • ParticipantJoined SFU event now will carry is_pinned property so you can use it for updating the client-side state correctly

Implementation

React PR

  • Add a property is_pinned in ParticipantJoinedEvent

Pin Resolution

A new PinManager has been introduced as the central owner of:

  • local pin state
  • server pin state
  • reconnect-aware pin resolution

The main entry points are:

image

New PinManager class (pinning/PinManager.kt)

  • Centralises all pin mutations — pin(), unpin(), setServerPins(), onParticipantJoined() — that were previously scattered across CallState
  • Mirrors JS SDK's setServerSidePins: assigns pinnedAt = now + (pins.size - index) so the server's priority order is preserved as a descending timestamp sequence
  • Preserves an existing pinnedAt when a participant is already server-pinned (JS guard: typeof participant.pin?.pinnedAt !== 'number')
  • Reconnection support: builds a dual-key lookup (sessionId → exact match, userId → fallback). Duplicate userId entries are dropped so the wrong participant is never pinned on reconnect

CallState cleanup

  • pin() / unpin() delegate to PinManager
  • updateServerSidePins() private function removed — replaced by PinManager.setServerPins()
  • SessionId typealias introduced for readability

There are auto generated files, which are not the core part of this PR

  1. SignalLostSignalingServiceDecorator.kt
  2. SignalingServiceTracerDecorator.kt
  3. go files

🎨 UI Changes

None

Testing

Scenario A: Pin propagation on join

  • Start a new call from Web with User A
  • User B joins the call
  • On User A, perform "Pin Everyone" on User B
  • User C joins the call
  • Verify User C sees User B pinned
    Status: Passed ✅

Scenario A – Rejoin Flow (User C)

  • User C leaves the call
  • User C rejoins
  • Verify On User C still sees User B pinned
    Status: Passed ✅

Scenario A – Rejoin Flow (User B)

Step 1: User B leaves

  • User B leaves the call
  • Verify on User C that no participants are pinned
    Status: Passed ✅

Step 2: User B rejoins flow

  • User B rejoins the call
  • Verify on User B that User B appears pinned
    Observed:
  • User B is not pinned
  • Spotlight UI is rendered
    Status: Passed (expected due to known issue) ✅
    Reason: Pins from JoinCallResponseEvent may contain a different sessionId, but the fallback userId matching successfully restores the pin association.

Step 3: User B rejoins flow
State on User C

  • Verify on User C still sees User B pinned
    Status: Passed ✅

Summary by CodeRabbit

  • New Features

    • Added metrics and telemetry support for media performance monitoring and RTP statistics tracking.
    • Enhanced participant pinning functionality for improved video call organization and state management.
    • Added support for Python and Vision Agents SDK types.
  • Improvements

    • Expanded participant information tracking with WebRTC version details and media negotiation identifiers.

Review Change Stack

@rahul-lohra rahul-lohra self-assigned this May 15, 2026
@rahul-lohra rahul-lohra requested a review from a team as a code owner May 15, 2026 07:59
@rahul-lohra rahul-lohra added the pr:bug Fixes a bug label May 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled (or ignored for dependabot PRs).

🎉 Great job! This PR is ready for review.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Walkthrough

This PR introduces SFU metrics reporting via a new SendMetrics RPC endpoint and refactors participant pinning into a centralized PinManager that tracks local and server-side pins with timestamp-based priority resolution, including proto schema extensions, signaling decorator support, state integration, and UI updates.

Changes

SFU Metrics and Participant Pinning

Layer / File(s) Summary
Proto schema contracts and code generation
generate-sfu.sh, stream-video-android-core/src/main/proto/video/sfu/event/events.proto, stream-video-android-core/src/main/proto/video/sfu/models/models.proto, stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal.proto, stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal.twirp.go, stream-video-android-core/api/stream-video-android-core.api
Proto definitions extended with RTP telemetry messages (InboundRtp, OutboundRtp, RemoteInboundRtp, RemoteOutboundRtp), SendMetrics RPC endpoint, negotiation_id and webrtc_version fields, plus new SDK and capability constants. Generated Kotlin API surface and Twirp Go code reflect these additions. Build script automates proto file generation from remote repository.
Metrics support in signaling decorators
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/utils/RetryableSignalingServiceDecorator.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/trace/SignalingServiceTracerDecorator.kt
Signaling decorators override sendMetrics to forward RPC calls through existing retry and tracing infrastructure.
Pin management state engine
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pinning/PinManager.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pinning/PinUpdates.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/events/SfuDataEvent.kt, stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/pinning/PinManagerTest.kt
New PinManager centralizes local and server pin tracking using StateFlow maps with TimeProvider abstraction. PinEntry (renamed from PinUpdateAtTime) stores pinned participants with timestamps. Server pins resolved via sessionId with userId fallback for reconnects, preserving priority order. ParticipantJoinedEvent extended with isPinned field. Comprehensive test suite verifies local/server operations, fallback behavior, and timestamp preservation.
CallState pin management integration
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt, stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
CallState refactored to delegate pin operations to PinManager instead of internal _serverPins/_localPins fields. Event handlers wired to setServerPins, onParticipantJoined, and onParticipantLeft. pinnedParticipants StateFlow combines manager's local and server pin maps. SessionId typealias added for type safety.
Event mapping and UI integration
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/signal/socket/RTCEventMapper.kt, stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt, stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/CallStateTest.kt, stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/SfuSocketStateEventTest.kt
Event mapper passes is_pinned flag from SFU events. UI component observes pinnedParticipants via collectAsStateWithLifecycle. Tests updated to construct ParticipantJoinedEvent with isPinned and use pinManager.updateLocalPins for setup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

pr:new-feature

Suggested reviewers

  • aleksandar-apostolov

🐰 Metrics flow and pins aligned,
Through manager's state, both flows entwined,
Server-side priority preserved with care,
RTP telemetry in the air! 📊📌

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive with Goal, Implementation, Testing sections that follow the template structure, though UI Changes section is minimal and some template checklist items are not completed.
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.
Title check ✅ Passed The title clearly and concisely summarizes the main change: improving pinned participant state handling when participants rejoin with new session IDs, which is the central purpose of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/rahullohra/pinned-users-2

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
generate-sfu.sh (2)

5-5: 💤 Low value

Consider supporting HTTPS URLs for broader environment compatibility.

The SSH URL git@github.com:GetStream/protocol.git requires SSH key authentication, which may not be configured in all CI/CD environments. Consider parameterizing the URL or defaulting to HTTPS (https://github.com/GetStream/protocol.git) for wider compatibility.

🤖 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 `@generate-sfu.sh` at line 5, The REPO_URL variable is hardcoded to an SSH URL
which can fail in CI; update the script to accept an override (e.g., via
environment variable or script arg) and default to an HTTPS URL; specifically
change the REPO_URL assignment (REPO_URL) to use something like
${REPO_URL:-https://github.com/GetStream/protocol.git} or parse a passed
parameter so callers can supply an SSH URL when needed.

23-39: 💤 Low value

Simplify git checkout logic for the default branch case.

When REFERENCE_TYPE is "branch" (the default), line 23's git clone --branch "$REFERENCE_VALUE" already checks out the specified branch, making the subsequent git checkout "$REFERENCE_VALUE" at line 29 redundant. For tags and commits, the shallow clone won't fetch the ref, so the checkout logic is still needed for those cases.

Consider restructuring to only checkout when needed:

♻️ Proposed refactor
 git clone --depth=1 --branch "$REFERENCE_VALUE" "$REPO_URL" "$CLONE_DIR"
 
 cd "$CLONE_DIR"
 
-# Step 3: Checkout to the correct branch, tag, or commit
 if [ "$REFERENCE_TYPE" == "branch" ]; then
-    git checkout "$REFERENCE_VALUE"
+    # Already on the correct branch from clone
+    :
 elif [ "$REFERENCE_TYPE" == "tag" ]; then
     git fetch --tags
     git checkout "tags/$REFERENCE_VALUE"
🤖 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 `@generate-sfu.sh` around lines 23 - 39, The branch-case checkout is redundant
because git clone --branch "$REFERENCE_VALUE" already checks out the branch;
remove the branch branch (the branch branch of the if) or change the logic so
you only run git checkout for tag and commit cases (keep the tag branch using
git fetch --tags + git checkout "tags/$REFERENCE_VALUE" and the commit branch
using git fetch --depth=1 origin "$REFERENCE_VALUE" + git checkout
"$REFERENCE_VALUE"), and ensure the ELSE still prints the invalid REFERENCE_TYPE
error; reference REFERENCE_TYPE, REFERENCE_VALUE, the initial git clone --branch
invocation, git fetch --tags, and git fetch --depth=1 origin when making the
change.
stream-video-android-core/api/stream-video-android-core.api (1)

11944-11958: ⚡ Quick win

Breaking constructor change on public ParticipantJoinedEvent — add a default for isPinned and call out in release notes.

The primary constructor went from (Participant, String) to (Participant, String, Z). While this is an SDK-emitted event (consumers typically observe rather than construct it — consistent with the prior guidance on internal-only-constructed event types in this file), any external/test code that instantiates ParticipantJoinedEvent positionally (e.g., in user-side unit tests or fixtures) will break at source and binary level.

Consider giving isPinned a default value in the Kotlin data class so source compatibility is preserved for existing Kotlin callers, and mention the constructor shape change in the release notes/migration guide.

- data class ParticipantJoinedEvent(val participant: Participant, val callCid: String, val isPinned: Boolean) : SfuDataEvent()
+ data class ParticipantJoinedEvent(
+     val participant: Participant,
+     val callCid: String,
+     val isPinned: Boolean = false,
+ ) : SfuDataEvent()

Based on learnings: prior guidance for this .api file (PR 1588, LocalCallMissedEvent) is that breaking changes to internal/SDK-instantiated event constructors are acceptable but should be documented in release notes and the public-surface impact should be minimized.

🤖 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 `@stream-video-android-core/api/stream-video-android-core.api` around lines
11944 - 11958, The public data class ParticipantJoinedEvent changed its primary
constructor from (Participant, String) to (Participant, String, Boolean) causing
a binary/source break; restore source compatibility by giving the new Boolean
parameter a default (e.g., isPinned: Boolean = false) in the
ParticipantJoinedEvent declaration so existing positional callers continue to
compile, ensure the generated copy/copy$default signatures remain compatible,
and update release notes/migration guide to call out the constructor shape
change and the default value.
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/trace/SignalingServiceTracerDecorator.kt (1)

102-103: ⚡ Quick win

Consider adding a comment explaining why sendMetrics bypasses tracing.

Unlike most other RPC methods (which use the traced() wrapper), sendMetrics calls the target directly without tracing—matching the pattern of sendStats (line 99). If this is intentional to reduce log volume for high-frequency metrics reporting, a brief comment would clarify the design choice and prevent confusion.

For example:

// Skip tracing for sendMetrics to avoid high-frequency log noise
override suspend fun sendMetrics(sendMetricsRequest: SendMetricsRequest): SendMetricsResponse =
    target.sendMetrics(sendMetricsRequest)
🤖 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
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/trace/SignalingServiceTracerDecorator.kt`
around lines 102 - 103, The sendMetrics method in
SignalingServiceTracerDecorator directly delegates to target.sendMetrics without
using the traced() wrapper (like sendStats does), which is unclear to readers;
add a concise inline comment above the sendMetrics override explaining that
tracing is intentionally skipped to avoid high-frequency log noise (or whatever
the intended rationale) so future maintainers understand why sendMetrics
bypasses traced(), referencing the sendMetrics override and the traced() wrapper
for context.
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/utils/RetryableSignalingServiceDecorator.kt (1)

122-123: ⚡ Quick win

Add a comment documenting why sendMetrics uses a null error extractor.

SendMetricsResponse is an empty proto message with no error field, making { null } the only valid extractor. Unlike sendStats (which uses { it.error }) where the response includes a models.Error error field, metrics responses cannot contain SFU errors. A brief inline comment (e.g., retryCall({ null }) // SendMetricsResponse has no error field) will clarify this for readers comparing against other RPC methods.

🤖 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
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/utils/RetryableSignalingServiceDecorator.kt`
around lines 122 - 123, The retry call for sendMetrics currently passes a null
error extractor (retryCall({ null }) { decorated.sendMetrics(sendMetricsRequest)
}) which is necessary because SendMetricsResponse is an empty proto with no
error field; add a brief inline comment on that line (e.g., "//
SendMetricsResponse has no error field; extractor must be null") to explain why
this differs from sendStats (which uses { it.error }) so readers understand the
choice; update the comment next to the retryCall invocation in
RetryableSignalingServiceDecorator.sendMetrics.
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt (1)

1097-1102: ⚡ Quick win

Simplify participant-left pin cleanup.

Lines 1097-1099 explicitly check localPins and call pinManager.unpin, but pinManager.onParticipantLeft (called at line 1102) already performs the same check and calls unpin internally (see PinManager.kt lines 52-56). The explicit unpin call is redundant.

♻️ Simplify by removing redundant unpin call
-                if (pinManager.localPins.value.containsKey(sessionId)) {
-                    // Remove any pins for the participant
-                    pinManager.unpin(sessionId)
-                }
-
                 pinManager.onParticipantLeft(sessionId)
🤖 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
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt`
around lines 1097 - 1102, The code redundantly calls pinManager.unpin(sessionId)
after checking pinManager.localPins when pinManager.onParticipantLeft(sessionId)
already handles that; remove the explicit containsKey check and the
pinManager.unpin(sessionId) call in CallState.kt and keep only the single
pinManager.onParticipantLeft(sessionId) invocation so cleanup is performed via
PinManager.onParticipantLeft.
🤖 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
`@stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/CallStateTest.kt`:
- Around line 131-136: The test data has an inconsistent userId: update the
PinUpdate userId used in the call.state.pinManager.updateLocalPins call so it
matches the ParticipantState userId ("1") — locate the call to
call.state.pinManager.updateLocalPins that creates a PinEntry with
PinUpdate("1", "userId") and change the second argument to "1" (i.e.,
PinUpdate("1", "1")) so PinUpdate, PinEntry and the ParticipantState share the
same userId.

---

Nitpick comments:
In `@generate-sfu.sh`:
- Line 5: The REPO_URL variable is hardcoded to an SSH URL which can fail in CI;
update the script to accept an override (e.g., via environment variable or
script arg) and default to an HTTPS URL; specifically change the REPO_URL
assignment (REPO_URL) to use something like
${REPO_URL:-https://github.com/GetStream/protocol.git} or parse a passed
parameter so callers can supply an SSH URL when needed.
- Around line 23-39: The branch-case checkout is redundant because git clone
--branch "$REFERENCE_VALUE" already checks out the branch; remove the branch
branch (the branch branch of the if) or change the logic so you only run git
checkout for tag and commit cases (keep the tag branch using git fetch --tags +
git checkout "tags/$REFERENCE_VALUE" and the commit branch using git fetch
--depth=1 origin "$REFERENCE_VALUE" + git checkout "$REFERENCE_VALUE"), and
ensure the ELSE still prints the invalid REFERENCE_TYPE error; reference
REFERENCE_TYPE, REFERENCE_VALUE, the initial git clone --branch invocation, git
fetch --tags, and git fetch --depth=1 origin when making the change.

In `@stream-video-android-core/api/stream-video-android-core.api`:
- Around line 11944-11958: The public data class ParticipantJoinedEvent changed
its primary constructor from (Participant, String) to (Participant, String,
Boolean) causing a binary/source break; restore source compatibility by giving
the new Boolean parameter a default (e.g., isPinned: Boolean = false) in the
ParticipantJoinedEvent declaration so existing positional callers continue to
compile, ensure the generated copy/copy$default signatures remain compatible,
and update release notes/migration guide to call out the constructor shape
change and the default value.

In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/utils/RetryableSignalingServiceDecorator.kt`:
- Around line 122-123: The retry call for sendMetrics currently passes a null
error extractor (retryCall({ null }) { decorated.sendMetrics(sendMetricsRequest)
}) which is necessary because SendMetricsResponse is an empty proto with no
error field; add a brief inline comment on that line (e.g., "//
SendMetricsResponse has no error field; extractor must be null") to explain why
this differs from sendStats (which uses { it.error }) so readers understand the
choice; update the comment next to the retryCall invocation in
RetryableSignalingServiceDecorator.sendMetrics.

In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt`:
- Around line 1097-1102: The code redundantly calls pinManager.unpin(sessionId)
after checking pinManager.localPins when pinManager.onParticipantLeft(sessionId)
already handles that; remove the explicit containsKey check and the
pinManager.unpin(sessionId) call in CallState.kt and keep only the single
pinManager.onParticipantLeft(sessionId) invocation so cleanup is performed via
PinManager.onParticipantLeft.

In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/trace/SignalingServiceTracerDecorator.kt`:
- Around line 102-103: The sendMetrics method in SignalingServiceTracerDecorator
directly delegates to target.sendMetrics without using the traced() wrapper
(like sendStats does), which is unclear to readers; add a concise inline comment
above the sendMetrics override explaining that tracing is intentionally skipped
to avoid high-frequency log noise (or whatever the intended rationale) so future
maintainers understand why sendMetrics bypasses traced(), referencing the
sendMetrics override and the traced() wrapper for context.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 50da1809-e10f-4c41-bb83-31f725cc762e

📥 Commits

Reviewing files that changed from the base of the PR and between 09cb9c9 and 34bd1f5.

⛔ Files ignored due to path filters (6)
  • stream-video-android-core/src/main/proto/video/sfu/event/events.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/event/events_vtproto.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/models/models.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/models/models_vtproto.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal.pb.go is excluded by !**/*.pb.go
  • stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (18)
  • generate-sfu.sh
  • stream-video-android-core/api/stream-video-android-core.api
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/Call.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/CallState.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/signal/socket/RTCEventMapper.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/utils/RetryableSignalingServiceDecorator.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/events/SfuDataEvent.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pinning/PinManager.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/pinning/PinUpdates.kt
  • stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/trace/SignalingServiceTracerDecorator.kt
  • stream-video-android-core/src/main/proto/video/sfu/event/events.proto
  • stream-video-android-core/src/main/proto/video/sfu/models/models.proto
  • stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal.proto
  • stream-video-android-core/src/main/proto/video/sfu/signal_rpc/signal.twirp.go
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/CallStateTest.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/SfuSocketStateEventTest.kt
  • stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/pinning/PinManagerTest.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt

Comment on lines +131 to 136
call.state.pinManager.updateLocalPins(
mutableMapOf(
"1" to PinEntry(
PinUpdate("1", "userId"), OffsetDateTime.now(Clock.systemUTC()), PinType.Local,
),
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix userId inconsistency in test data.

The PinUpdate uses userId = "userId" (literal string), but the corresponding ParticipantState at line 156 uses userId = "1". While the test may pass due to sessionId matching, this inconsistency reduces test clarity and could cause confusion.

🔧 Proposed fix
         call.state.pinManager.updateLocalPins(
             mutableMapOf(
                 "1" to PinEntry(
-                    PinUpdate("1", "userId"), OffsetDateTime.now(Clock.systemUTC()), PinType.Local,
+                    PinUpdate("1", "1"), OffsetDateTime.now(Clock.systemUTC()), PinType.Local,
                 ),
             ),
         )
📝 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
call.state.pinManager.updateLocalPins(
mutableMapOf(
"1" to PinEntry(
PinUpdate("1", "userId"), OffsetDateTime.now(Clock.systemUTC()), PinType.Local,
),
),
call.state.pinManager.updateLocalPins(
mutableMapOf(
"1" to PinEntry(
PinUpdate("1", "1"), OffsetDateTime.now(Clock.systemUTC()), PinType.Local,
),
),
🤖 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
`@stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/CallStateTest.kt`
around lines 131 - 136, The test data has an inconsistent userId: update the
PinUpdate userId used in the call.state.pinManager.updateLocalPins call so it
matches the ParticipantState userId ("1") — locate the call to
call.state.pinManager.updateLocalPins that creates a PinEntry with
PinUpdate("1", "userId") and change the second argument to "1" (i.e.,
PinUpdate("1", "1")) so PinUpdate, PinEntry and the ParticipantState share the
same userId.

@rahul-lohra rahul-lohra changed the title Improve pinned participant state on rejoin with new session [AND-1144] Improve pinned participant state on rejoin with new session May 15, 2026
@rahul-lohra rahul-lohra changed the title [AND-1144] Improve pinned participant state on rejoin with new session Improve pinned participant state on rejoin with new session May 15, 2026
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
76.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@github-actions
Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 12.04 MB 12.05 MB 0.02 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.68 MB 0.00 MB 🟢
stream-video-android-ui-compose 6.27 MB 6.27 MB 0.00 MB 🟢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:bug Fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant