/// Ephemeral, in-memory state published straight from the location callback. /// /// Ported from `com.rippr.LiveTelemetry` and `com.rippr.UploadStatus`. Both were Kotlin /// `object` singletons over `StateFlow`; here they are small value-holding broadcast /// streams so the pure layer stays free of Flutter and Riverpod. The UI layer wraps /// these in providers rather than the other way round. /// /// Everything in this file is deliberately **not** persisted. Recording state lives in /// the database because it must survive process death; these must not, because a stale /// value from a previous process would be actively misleading. library; import 'dart:async'; /// A stream that also remembers its current value, so a late subscriber is not blind /// until the next emission. class _ValueStream { _ValueStream(this._value); final _controller = StreamController.broadcast(); T _value; T get value => _value; Stream get stream => _controller.stream; void set(T next) { _value = next; if (!_controller.isClosed) _controller.add(next); } Future dispose() => _controller.close(); } /// The most recent fix, published straight from the location callback. /// /// The Trip row is only written every ~2 s and carries *max* speed, not current, so the /// recording screen cannot show a live speedo from it. /// /// This existing precisely because v2.0 shipped max speed as the headline figure and it /// read as a frozen screen on a real ride — see `docs/TESTING.md` in the native repo. class LiveTelemetry { LiveTelemetry._(); static final instance = LiveTelemetry._(); final _speedKmh = _ValueStream(0.0); final _accuracyM = _ValueStream(0.0); double get speedKmh => _speedKmh.value; double get accuracyM => _accuracyM.value; Stream get speedStream => _speedKmh.stream; Stream get accuracyStream => _accuracyM.stream; void update(double speedKmh, double accuracyM) { _speedKmh.set(speedKmh); _accuracyM.set(accuracyM); } void clear() { _speedKmh.set(0.0); _accuracyM.set(0.0); } } /// Last-seen upload failure, surfaced on the recording screen. class UploadStatus { UploadStatus._(); static final instance = UploadStatus._(); final _lastError = _ValueStream(null); String? get lastError => _lastError.value; Stream get stream => _lastError.stream; void setError(String? message) => _lastError.set(message); }