Skip to content

fix(llc): replace live location polling with one-shot expiry timers - #2863

Open
VelikovPetar wants to merge 2 commits into
masterfrom
fix/FLU-488_live_location_one_shot_timers
Open

fix(llc): replace live location polling with one-shot expiry timers#2863
VelikovPetar wants to merge 2 commits into
masterfrom
fix/FLU-488_live_location_one_shot_timers

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-488

Github Issue: #

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Live-location expiry was detected by a Timer.periodic(seconds: 1) scan in both ClientState (current user's own locations) and ChannelClientState (other users' locations). Every second it re-scanned all active live locations and synthesized a location.expired event for any that were past endAt — which could emit the same expiry event repeatedly, and always wastes a per-second wakeup per client/channel.

This replaces that polling with a one-shot Timer per active live location, scheduled to fire exactly once at the location's endAt.

What changed

  • New internal LiveLocationExpirationScheduler (packages/stream_chat/lib/src/client/live_location_expiration_scheduler.dart, @internal, not exported). It keeps one timer per location keyed by message id and reconciles on each change to the active set:
    • unchanged endAt → reuse the timer (only refresh coordinates) — a moving location doesn't churn its timer;
    • changed endAt (extend / stop-early) → cancel and re-arm at the new time;
    • removed → cancel;
    • on fire it re-checks isExpired and re-arms (rather than emitting) if the clock moved back / endAt was extended.
  • ClientState drives it from the activeLiveLocations setter; ChannelClientState drives it off activeLiveLocationsStream. The channel keeps the current-user skip (its own locations are handled by the client layer) at emit time. Both cancel all timers on dispose().

Effect

  • location.expired now fires once, at endAt, instead of repeating on the 1-second scan.
  • Removes the per-second wakeup for every client/channel (channels with no live locations now hold zero timers).

Breaking-change verdict: internal/behavioral only — no public API changes (all touched members are private / @internal), non-breaking.

Cross-SDK: matches the one-shot-per-location approach used by iOS (ActiveLiveLocationsEndTimeTracker) and the JS/RN client (LiveLocationManager). Reschedule-on-endAt-change is handled here (the server supports extending endAt and emits nothing at the natural expiry moment, so expiry is client-inferred).

Test instructions

  • New unit suite for the scheduler (fires once at endAt; ignores static/expired/no-id; reuses timer + refreshes coordinates on same-endAt update; reschedules on endAt change; cancels on removal/cancel()).
  • Extended the existing location groups in client_test.dart and channel_test.dart (own location auto-expires once; other user's location auto-expires once; current user's own location is skipped on the channel layer).
  • Verified manually on two Android devices (sender + receiver): fires once at endAt, no per-second churn, coordinate updates reuse the timer, stop-early cancels, correct owner/other split.

Screenshots / Videos

No UI changes.

Summary by CodeRabbit

  • Bug Fixes
    • Live locations now expire automatically at their configured end time.
    • Expiration events are emitted once per location, preventing repeated location.expired notifications.
    • Expired locations are removed from active state promptly.
    • Live-location updates continue to reflect refreshed coordinates and changed expiration times.
    • Locations that are removed or canceled no longer trigger expiration events.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 46ae32d3-f0b1-4b2d-92f1-b2372e24b018

📥 Commits

Reviewing files that changed from the base of the PR and between a7e26f0 and 28118d6.

📒 Files selected for processing (7)
  • packages/stream_chat/CHANGELOG.md
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/lib/src/client/client.dart
  • packages/stream_chat/lib/src/client/live_location_expiration_scheduler.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat/test/src/client/client_test.dart
  • packages/stream_chat/test/src/client/live_location_expiration_scheduler_test.dart

📝 Walkthrough

Walkthrough

Changes

Live location expiration

Layer / File(s) Summary
Expiration scheduler and unit coverage
packages/stream_chat/lib/src/client/live_location_expiration_scheduler.dart, packages/stream_chat/test/src/client/live_location_expiration_scheduler_test.dart
Adds keyed one-shot timers for active locations. The scheduler handles coordinate updates, changed expiration times, removed locations, expired locations, and cancellation.
Client expiration integration
packages/stream_chat/lib/src/client/client.dart, packages/stream_chat/test/src/client/client_test.dart
Client state schedules active locations, dispatches synthetic location.expired events, removes expired locations, and cancels timers during disposal.
Channel expiration integration
packages/stream_chat/lib/src/client/channel.dart, packages/stream_chat/test/src/client/channel_test.dart, packages/stream_chat/CHANGELOG.md
Channel state schedules active locations and dispatches one event for other users’ expired locations. Current-user locations remain excluded from channel-side automatic expiration. The changelog records the fix.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClientState
  participant LiveLocationExpirationScheduler
  participant EventDispatcher
  ClientState->>LiveLocationExpirationScheduler: schedule active locations
  LiveLocationExpirationScheduler->>ClientState: onExpired(location)
  ClientState->>EventDispatcher: dispatch synthetic location.expired
Loading

Suggested reviewers: xsahil03x, renefloor

🚥 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 clearly and concisely describes replacing live-location polling with one-shot expiry timers.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/FLU-488_live_location_one_shot_timers

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.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@VelikovPetar
VelikovPetar force-pushed the fix/FLU-488_live_location_one_shot_timers branch from be022b0 to 71fd215 Compare August 3, 2026 11:34
…cation_one_shot_timers

# Conflicts:
#	packages/stream_chat/CHANGELOG.md
@VelikovPetar
VelikovPetar requested a review from a team August 3, 2026 11:43
@VelikovPetar
VelikovPetar marked this pull request as ready for review August 3, 2026 11:43
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.97%. Comparing base (a7e26f0) to head (28118d6).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2863      +/-   ##
==========================================
+ Coverage   72.86%   72.97%   +0.10%     
==========================================
  Files         429      430       +1     
  Lines       27698    27733      +35     
==========================================
+ Hits        20183    20237      +54     
+ Misses       7515     7496      -19     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant