fix: Merge self replay and refresh self telemetry

This commit is contained in:
Janez T
2026-03-24 19:07:44 +01:00
parent ba0f1fc141
commit 6978f24440
7 changed files with 159 additions and 48 deletions

View File

@@ -666,7 +666,23 @@ class MessagesProvider with ChangeNotifier {
final matchingSentReplayIndex = _findMatchingSentReplayIndex(finalMessage);
if (matchingSentReplayIndex != -1) {
_clearChannelSendWarning(_messages[matchingSentReplayIndex].id);
final existingId = _messages[matchingSentReplayIndex].id;
_clearChannelSendWarning(existingId);
if (contactLocationSnapshot != null) {
_messageContactLocations[existingId] = contactLocationSnapshot;
}
_messageReceptionDetails[existingId] =
MessageReceptionDetails.mergeDuplicate(
existing: _messageReceptionDetails[existingId],
incoming: receptionDetailsSnapshot,
);
final existingMessage = _messages[matchingSentReplayIndex];
_messages[matchingSentReplayIndex] = existingMessage.copyWith(
pathLen: finalMessage.pathLen > 0 ? finalMessage.pathLen : existingMessage.pathLen,
pathBytes: finalMessage.pathBytes ?? existingMessage.pathBytes,
);
_persistMessages();
return;
}
_messages.add(finalMessage);
@@ -1214,9 +1230,7 @@ class MessagesProvider with ChangeNotifier {
for (final message in messages) {
final occurrenceCount = _messageOccurrenceCount(message);
final existingIndex = entries.indexWhere(
(entry) =>
entry.message.text == message.text &&
_matchesDuplicateScope(entry.message, message),
(entry) => _shouldCollapseDisplayMessage(entry.message, message),
);
if (existingIndex == -1) {
@@ -1234,6 +1248,15 @@ class MessagesProvider with ChangeNotifier {
return entries;
}
bool _shouldCollapseDisplayMessage(Message existing, Message message) {
if (existing.isSentMessage || message.isSentMessage) {
return false;
}
return existing.text == message.text &&
_matchesDuplicateScope(existing, message);
}
int _messageOccurrenceCount(Message message) =>
_messageReceptionDetails[message.id]?.receivedCopies ?? 1;

View File

@@ -14,7 +14,7 @@ enum SensorRefreshState { idle, refreshing, success, timeout, unavailable }
class SensorsProvider with ChangeNotifier {
static const Duration _successStateRetention = Duration(minutes: 1);
static const int selfAutoRefreshMinutes = 1;
static const Duration selfAutoRefreshInterval = Duration(seconds: 30);
static const String _watchedSensorsKey = 'watched_sensor_keys';
static const String _visibleSensorMetricsKey = 'visible_sensor_metrics';
static const String _fieldSpanKey = 'sensor_field_spans';
@@ -374,18 +374,18 @@ class SensorsProvider with ChangeNotifier {
return List<String>.unmodifiable(dueKeys);
}
bool _isRefreshDue(
bool _isRefreshDueForInterval(
String publicKeyHex, {
required int minutes,
required Duration interval,
required DateTime refreshTime,
}) {
if (minutes <= 0) {
if (interval <= Duration.zero) {
return false;
}
final lastRefreshAt = _lastRefreshAttemptAt[publicKeyHex];
return lastRefreshAt == null ||
refreshTime.difference(lastRefreshAt) >= Duration(minutes: minutes);
refreshTime.difference(lastRefreshAt) >= interval;
}
Future<void> toggleMetric(
@@ -769,9 +769,9 @@ class SensorsProvider with ChangeNotifier {
final self = selfContact(contactsProvider, connectionProvider);
if (self != null &&
!dueKeys.contains(self.publicKeyHex) &&
_isRefreshDue(
_isRefreshDueForInterval(
self.publicKeyHex,
minutes: selfAutoRefreshMinutes,
interval: selfAutoRefreshInterval,
refreshTime: refreshTime,
)) {
dueKeys.insert(0, self.publicKeyHex);

View File

@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import '../models/contact.dart';
import '../providers/connection_provider.dart';
import '../providers/contacts_provider.dart';
import '../providers/map_provider.dart';
import '../providers/sensors_provider.dart';
import '../widgets/sensors/bthome_met_history_sheet.dart';
import '../widgets/sensors/sensor_telemetry_card.dart';
@@ -21,7 +22,10 @@ class SensorsTab extends StatefulWidget {
}
class _SensorsTabState extends State<SensorsTab> {
static const Duration _autoRefreshTickInterval = Duration(seconds: 30);
Timer? _minuteTicker;
final Map<String, DateTime> _lastCenteredTelemetryAtBySensor =
<String, DateTime>{};
@override
void initState() {
@@ -61,22 +65,8 @@ class _SensorsTabState extends State<SensorsTab> {
return;
}
final now = DateTime.now();
final nextMinute = DateTime(
now.year,
now.month,
now.day,
now.hour,
now.minute + 1,
);
final delay = nextMinute.difference(now);
_minuteTicker = Timer(delay, () {
if (!mounted) return;
_minuteTicker = Timer.periodic(_autoRefreshTickInterval, (_) {
unawaited(_handleMinuteTick());
_minuteTicker = Timer.periodic(const Duration(minutes: 1), (_) {
unawaited(_handleMinuteTick());
});
});
}
@@ -225,6 +215,65 @@ class _SensorsTabState extends State<SensorsTab> {
);
}
void _maybeCenterMapOnTelemetryUpdate(
Iterable<String> sensorKeys, {
required SensorsProvider sensorsProvider,
required ContactsProvider contactsProvider,
required ConnectionProvider connectionProvider,
}) {
if (!widget.isActive) {
return;
}
Contact? latestContact;
DateTime? latestTimestamp;
for (final key in sensorKeys) {
final contact = sensorsProvider.contactForDisplay(
key,
contactsProvider: contactsProvider,
connectionProvider: connectionProvider,
);
final timestamp = contact?.telemetry?.timestamp;
final location = contact?.displayLocation;
if (contact == null || timestamp == null || location == null) {
continue;
}
final previousTimestamp = _lastCenteredTelemetryAtBySensor[key];
if (previousTimestamp != null && !timestamp.isAfter(previousTimestamp)) {
continue;
}
if (latestTimestamp == null || timestamp.isAfter(latestTimestamp)) {
latestContact = contact;
latestTimestamp = timestamp;
}
}
if (latestContact == null || latestTimestamp == null) {
return;
}
_lastCenteredTelemetryAtBySensor[latestContact.publicKeyHex] =
latestTimestamp;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted || !widget.isActive) {
return;
}
final location = latestContact!.displayLocation;
if (location == null) {
return;
}
context.read<MapProvider>().navigateToLocation(
location: location,
zoom: 15.0,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -247,6 +296,16 @@ class _SensorsTabState extends State<SensorsTab> {
contactsProvider: contactsProvider,
connectionProvider: connectionProvider,
);
final displayKeys = <String>[
...?selfDisplayKey == null ? null : <String>[selfDisplayKey],
...watchedKeys,
];
_maybeCenterMapOnTelemetryUpdate(
displayKeys,
sensorsProvider: sensorsProvider,
contactsProvider: contactsProvider,
connectionProvider: connectionProvider,
);
final hasAnyCards =
selfDisplayKey != null || watchedKeys.isNotEmpty;