From ae03c376dfb20ede50e2a234aa8a4c6b932adc60 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Thu, 23 Apr 2026 02:38:06 +0100 Subject: [PATCH 1/2] package_name --- lib/widgets/reports_webview.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/widgets/reports_webview.dart b/lib/widgets/reports_webview.dart index 3e12edb..295ddc8 100644 --- a/lib/widgets/reports_webview.dart +++ b/lib/widgets/reports_webview.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:http/http.dart' as http; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; import '../l10n/app_localizations.dart'; @@ -63,9 +64,10 @@ class _ReportsWebViewState extends State { } final defaultUa = await InAppWebViewController.getDefaultUserAgent(); + final packageInfo = await PackageInfo.fromPlatform(); if (!mounted) return; setState(() { - _userAgent = '$defaultUa com.fleetmap.jsrastreamento'; + _userAgent = '$defaultUa ${packageInfo.packageName}'; _initialUri = WebUri('$traccarBaseUrl/reports'); }); } From 3c40374e7338825107636a05327ed203cf93b340 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Thu, 23 Apr 2026 03:06:56 +0100 Subject: [PATCH 2/2] bottom sheet: reflect merged secondary device status Pass _visibleDevices into the bottom sheet and invalidate its cache on status change so WebSocket updates arriving on the secondary device id propagate to the primary that the sheet is showing. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/models/device.dart | 18 +++++++++++ lib/pages/main_page.dart | 70 +++++++++++++++++++++++++--------------- pubspec.yaml | 2 +- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/lib/models/device.dart b/lib/models/device.dart index ce7a937..d0338ae 100644 --- a/lib/models/device.dart +++ b/lib/models/device.dart @@ -49,6 +49,24 @@ class Device { ); } + Device copyWith({String? status}) { + return Device( + id: id, + name: name, + uniqueId: uniqueId, + status: status ?? this.status, + disabled: disabled, + lastUpdate: lastUpdate, + positionId: positionId, + groupId: groupId, + phone: phone, + model: model, + contact: contact, + category: category, + attributes: attributes, + ); + } + Map toJson() { return { 'id': id, diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index 5bd73c7..52435b5 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -65,43 +65,57 @@ class _MainPageState extends State { Map get _visibleDevices { if (_mergeSecondaryIds.isEmpty) return _devices; - return Map.fromEntries( + final result = Map.fromEntries( _devices.entries.where((e) => !_mergeSecondaryIds.contains(e.key)), ); + for (final merge in _deviceMerges) { + if (_useSecondary(merge)) { + final primary = result[merge.primaryDeviceId]; + final secondary = _devices[merge.secondaryDeviceId]; + if (primary != null && secondary != null) { + result[merge.primaryDeviceId] = primary.copyWith(status: secondary.status); + } + } + } + return result; } Map get _effectivePositions { if (_deviceMerges.isEmpty) return _positions; final result = Map.from(_positions); for (final merge in _deviceMerges) { - final primaryPos = _positions[merge.primaryDeviceId]; - final secondaryPos = _positions[merge.secondaryDeviceId]; - if (secondaryPos != null) { - final primaryTime = primaryPos?.fixTime ?? DateTime.fromMillisecondsSinceEpoch(0); - if (secondaryPos.fixTime.isAfter(primaryTime)) { - result[merge.primaryDeviceId] = Position( - id: secondaryPos.id, - deviceId: merge.primaryDeviceId, - fixTime: secondaryPos.fixTime, - serverTime: secondaryPos.serverTime, - valid: secondaryPos.valid, - latitude: secondaryPos.latitude, - longitude: secondaryPos.longitude, - altitude: secondaryPos.altitude, - speed: secondaryPos.speed, - course: secondaryPos.course, - address: secondaryPos.address, - accuracy: secondaryPos.accuracy, - network: secondaryPos.network, - batteryLevel: secondaryPos.batteryLevel, - attributes: secondaryPos.attributes, - ); - } + if (_useSecondary(merge)) { + final secondaryPos = _positions[merge.secondaryDeviceId]!; + result[merge.primaryDeviceId] = Position( + id: secondaryPos.id, + deviceId: merge.primaryDeviceId, + fixTime: secondaryPos.fixTime, + serverTime: secondaryPos.serverTime, + valid: secondaryPos.valid, + latitude: secondaryPos.latitude, + longitude: secondaryPos.longitude, + altitude: secondaryPos.altitude, + speed: secondaryPos.speed, + course: secondaryPos.course, + address: secondaryPos.address, + accuracy: secondaryPos.accuracy, + network: secondaryPos.network, + batteryLevel: secondaryPos.batteryLevel, + attributes: secondaryPos.attributes, + ); } } return result; } + bool _useSecondary(DeviceMerge merge) { + final secondaryPos = _positions[merge.secondaryDeviceId]; + if (secondaryPos == null) return false; + final primaryPos = _positions[merge.primaryDeviceId]; + final primaryTime = primaryPos?.fixTime ?? DateTime.fromMillisecondsSinceEpoch(0); + return secondaryPos.fixTime.isAfter(primaryTime); + } + Future _init() async { final devices = await _apiService.fetchDevices(); final devicesMap = {}; @@ -539,7 +553,7 @@ class _MainPageState extends State { // Device Bottom Sheet _BottomSheetBuilder( selectedDeviceId: _selectedDeviceId, - devices: _devices, + devices: _visibleDevices, positions: _effectivePositions, deviceMerges: _deviceMerges, onClose: _closeBottomSheet, @@ -681,6 +695,7 @@ class _BottomSheetBuilder extends StatefulWidget { class _BottomSheetBuilderState extends State<_BottomSheetBuilder> { int? _lastDeviceId; int? _lastPositionId; + String? _lastDeviceStatus; bool? _lastShowingRoute; int? _lastHighlightedSegmentFirstId; Widget? _cachedSheet; @@ -700,13 +715,15 @@ class _BottomSheetBuilderState extends State<_BottomSheetBuilder> { // Check if device, position, or route view changed final deviceChanged = selectedDeviceId != _lastDeviceId; final positionChanged = currentPositionId != _lastPositionId; + final statusChanged = device?.status != _lastDeviceStatus; final routeViewChanged = widget.showingRoute != _lastShowingRoute; final highlightedSegmentChanged = currentHighlightedFirstId != _lastHighlightedSegmentFirstId; // Only rebuild if selected device's data or view actually changed - if (deviceChanged || positionChanged || routeViewChanged || highlightedSegmentChanged || _cachedSheet == null) { + if (deviceChanged || positionChanged || statusChanged || routeViewChanged || highlightedSegmentChanged || _cachedSheet == null) { _lastDeviceId = selectedDeviceId; _lastPositionId = currentPositionId; + _lastDeviceStatus = device?.status; _lastShowingRoute = widget.showingRoute; _lastHighlightedSegmentFirstId = currentHighlightedFirstId; @@ -749,6 +766,7 @@ class _BottomSheetBuilderState extends State<_BottomSheetBuilder> { } else { _lastDeviceId = null; _lastPositionId = null; + _lastDeviceStatus = null; _lastShowingRoute = null; _lastHighlightedSegmentFirstId = null; _cachedSheet = null; diff --git a/pubspec.yaml b/pubspec.yaml index 32a2304..3c2428f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: manager description: "Track Fleet" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 3.17.6+1 +version: 3.18.6+1 environment: sdk: ^3.9.2