Draft
Conversation
…ean online indicator. Fix log messages and null checks for breakout room transition variable. Replaced the generic warning in _presenceRoomId with a descriptive message explaining the uncommon null state. Change isNullOrEmpty check to a strict null check.
…TransitionToBreakoutRoomId on successful Agora connection. Separate caching logic into _cachedJoinInfoRoomId. Add a timer that logs a warning every 5 seconds if a breakout room transition has not completed, indicating a possible connection issue.
…oes not confirm a breakout room connection within 10 seconds, cancel the transition, return the user to the main meeting, and display an error message.
…localized and semantic
…nected participants
… participant selection.
#282). Can't run more than minutely.
firebase-tools now requires Java 21+, but the workflow relied on the runner's default JDK (Java 17). Add actions/setup-java@v4 with Temurin 21 before the emulator steps to fix the version mismatch.
|
Visit the preview URL for this PR (updated for commit ab8f525): https://gen-hls-bkc-7627--pr303-mw-fix-ghost-user-cl-p3yywb2i.web.app (expires Thu, 05 Mar 2026 19:46:00 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: eed668cca81618d491d024574a8f8a6003deaa8d |
… tests (emulator port mismatch).
Contributor
|
Thanks Mike, I'll be able to review this within the next couple days. |
…s, streams now estimate in time-bounded range
…host cleanup interval
…mParticipantCount pre-check window fix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ghost user Cleanup #282
What is in this PR?
After #281 established the heartbeat and
mostRecentPresentTime, no backend mechanism detected when those updates stopped. Users who disconnected without explicitly leaving continued to appear as present participants, inflating room counts and voting populations. Additionally, room-based queries (breakoutRoomParticipantsStream,VoteToKick) did not filter byisPresent, and theUpdateLiveStreamParticipantCountcloud function's pre-check window excluded currently-running events, freezing livestream counts at 1.This PR adds a scheduled cleanup function, fixes the livestream count function, closes query filter gaps, fixes participant count displays outside the meeting, hardens breakout room transitions, and reduces heartbeat frequency by 4x.
Commit 1 -- 97db878: Presence heartbeat semantics and breakout room null safety
_presenceUpdaterconditions to a semantic boolean check (leftMeeting/enterMeetingPrescreen)_activeBreakoutRoomIdwith_presenceRoomIdgetter that falls back tocurrentBreakoutRoomIdafter Agora confirms connection, fixing breakout room grid cards showing 0 participantsCommits 2-4 -- 52e5fe5, 3768431, 18ed1d8: Breakout room transition timeout
_cachedJoinInfoRoomIdfrom_inTransitionToBreakoutRoomIdfor clean state trackingCommit 5 -- 9682417: Composite Indexes
isPresent + mostRecentPresentTimeindex (collection group) for the cleanup querycurrentBreakoutRoomId + isPresentindex (collection) for filtered room queriesCommit 6 -- 9a03aed: Filter
breakoutRoomParticipantsStreambyisPresentisPresent == trueto the Firestore query inbreakoutRoomParticipantsStream()Commit 7 -- 026def5: Filter
VoteToKickparticipants byisPresentisPresent == trueto the participant population query inVoteToKickCommit 8 -- d4f5f1d: Stale Participant Cleanup Function
WHERE isPresent == true AND mostRecentPresentTime < (now - 45s)isPresent: false, preservescurrentBreakoutRoomIdCleanupStaleParticipants()tomain.dartCommit 9 -- 71d098e: Unit Tests
Commit 10 -- f4849a9: Java 21 in GH Workflow
Commit 11 -- 26e1e58: Fix firebase emulator port mismatch errors
firebase.jsonconfiguration. Tests never ran in CI since then because no PRs modifiedfirebase/firestore/**files.Commit 12 -- ba9381b: Fix count display on admin panel and event cards
Commit 13 -- 2489f14: Fix participant estimates on out-of-meeting views
UpdateLiveStreamParticipantCountpre-check window changed from[now, now + 1 day]to[now - maxDuration x 1.5, now + 1 hour], derived from named constantsdurationInMinutesEventProvider,EventParticipantsList,ParticipantsDialog, andEventButtonnow filter byisPresentwhen live participants existCommit 14 -- 6184022: Reduce heartbeat frequency and tighten cleanup interval
html.window.onOnlinelistener for immediate heartbeat on network reconnectCleanupStaleParticipants: restructured to 6 passes per minute with 45s stale thresholdUpdateLiveStreamParticipantCount: update window widened from 19s to 40s to accommodate slower heartbeatCommit 15: Add test coverage for VoteToKick isPresent filter and pre-check window fix
UpdateLiveStreamParticipantCount: add test for a currently-running event (scheduledTime in the past), which was the exact scenario broken by the old pre-check windowVoteToKick: add test verifying ghost participants (isPresent: false) are excluded from the kick vote denominatorevent_test_utils: addcurrentBreakoutRoomIdparameter tojoinEvent()Commit 16: Fix cleanup test failures and remove dead code from test utilities
runCleanupPass(0)directly instead ofaction(MockEventContext()). Theaction()method schedules 6 passes over 50 seconds of real time, causing "fresh" timestamps set before the call to age past the 45s threshold by pass 5_runCleanupPasstorunCleanupPass(public) so tests can invoke a single pass without the scheduling delayno_leading_underscores_for_local_identifiersevent_test_utilsreferencing services that are not available in the test utility classChanges in the codebase
Presence timing constants
Interaction with existing cleanup (UpdatePresenceStatus)
The RTDB-based
UpdatePresenceStatuscontinues to operate. It fires on RTDB/status/{uid}changes and setsisPresent: falseANDcurrentBreakoutRoomId: null. The scheduled cleanup is a fallback for cases where:The two mechanisms are complementary, not conflicting. If RTDB cleanup fires first, the scheduled cleanup skips the participant (already
isPresent: false). If the scheduled cleanup fires first, RTDB cleanup will either skip it (via thelastUpdatedTimeguard) or harmlessly re-setisPresent: false.Key difference:
UpdatePresenceStatusclearscurrentBreakoutRoomIdtonull. The scheduled cleanup preserves it. Both behaviors are safe because all room-based queries now filter byisPresent.Three-layer disconnect detection
onBeforeUnload/dispose()onDisconnect()toUpdatePresenceStatusCleanupStaleParticipants(every 10s)Changes outside the codebase
None
Testing this PR
breakoutRoomParticipantsStreamandVoteToKickqueries return only present participantsonOnlineheartbeat restoresisPresent: truebefore cleanup firesUpdateLiveStreamParticipantCountpre-check window still includes itFuture Work
isPresentonly on join/leave/transition. Reduces Firestore writes by roughly 97%. Requires promotingCleanupStaleParticipantsand RTDB disconnect to primary detection and increasing stale threshold. Estimated effort: roughly 2 days.(status, isPresent, createdDate)index soParticipantsDialog._buildLiveStreamEventParticipantscan filter to present-only userscheckAdvanceMeetingGuideisPresentfilter: Currently mitigated by AgorapresentIdscross-reference; add for consistency