Add desktop map controls and improve zoom functionality across multiple screens

This commit is contained in:
Winston Lowe
2026-04-28 19:26:51 -07:00
parent 99c0ab7e22
commit eb50249b93
10 changed files with 468 additions and 50 deletions
+98 -3
View File
@@ -1,6 +1,6 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
@@ -304,6 +304,8 @@ class ChannelMessagePathMapScreen extends StatefulWidget {
class _ChannelMessagePathMapScreenState
extends State<ChannelMessagePathMapScreen> {
static const double _labelZoomThreshold = 8.5;
static const double _mapMinZoom = 2.0;
static const double _mapMaxZoom = 18.0;
final MapController _mapController = MapController();
Uint8List? _selectedPath;
@@ -330,6 +332,18 @@ class _ChannelMessagePathMapScreenState
}
}
@override
void dispose() {
_mapController.dispose();
super.dispose();
}
bool _isDesktopPlatform(TargetPlatform platform) {
return platform == TargetPlatform.linux ||
platform == TargetPlatform.windows ||
platform == TargetPlatform.macOS;
}
double _getPathDistance(List<LatLng> points) {
double totalDistance = 0.0;
final distanceCalculator = Distance();
@@ -357,6 +371,70 @@ class _ChannelMessagePathMapScreenState
});
}
void _zoomMapBy(double delta) {
final camera = _mapController.camera;
final nextZoom = (camera.zoom + delta)
.clamp(_mapMinZoom, _mapMaxZoom)
.toDouble();
_mapController.move(camera.center, nextZoom);
}
void _resetMapView({
required LatLng initialCenter,
required double initialZoom,
required LatLngBounds? bounds,
}) {
if (bounds != null) {
_mapController.fitCamera(
CameraFit.bounds(
bounds: bounds,
padding: const EdgeInsets.all(64),
maxZoom: 16,
),
);
return;
}
_mapController.move(initialCenter, initialZoom);
}
Widget _buildDesktopMapControls({
required LatLng initialCenter,
required double initialZoom,
required LatLngBounds? bounds,
}) {
return Positioned(
left: 16,
top: 16,
child: Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
onPressed: () => _resetMapView(
initialCenter: initialCenter,
initialZoom: initialZoom,
bounds: bounds,
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Consumer<MeshCoreConnector>(
@@ -372,6 +450,7 @@ class _ChannelMessagePathMapScreenState
primaryPath,
widget.message.pathVariants,
);
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
final selectedPathTmp = _resolveSelectedPath(
_selectedPath,
observedPaths,
@@ -451,10 +530,20 @@ class _ChannelMessagePathMapScreenState
padding: const EdgeInsets.all(64),
maxZoom: 16,
),
minZoom: 2.0,
maxZoom: 18.0,
minZoom: _mapMinZoom,
maxZoom: _mapMaxZoom,
interactionOptions: InteractionOptions(
flags: ~InteractiveFlag.rotate,
scrollWheelVelocity: isDesktop ? 0.012 : 0.005,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled(),
keyboardOptions: isDesktop
? const KeyboardOptions(
enableArrowKeysPanning: true,
enableWASDPanning: true,
enableRFZooming: true,
)
: const KeyboardOptions.disabled(),
),
onPositionChanged: (camera, hasGesture) {
final shouldShow = camera.zoom >= _labelZoomThreshold;
@@ -486,6 +575,12 @@ class _ChannelMessagePathMapScreenState
),
],
),
if (isDesktop)
_buildDesktopMapControls(
initialCenter: initialCenter,
initialZoom: initialZoom,
bounds: bounds,
),
if (observedPaths.length > 1)
_buildPathSelector(context, observedPaths, selectedIndex, (
index,
+6 -3
View File
@@ -492,8 +492,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
],
),
onTap: () async {
final unread =
connector.getUnreadCountForChannelIndex(channel.index);
final unread = connector.getUnreadCountForChannelIndex(
channel.index,
);
connector.markChannelRead(channel.index);
await Future.delayed(const Duration(milliseconds: 50));
if (context.mounted) {
@@ -1497,7 +1498,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
if (!context.mounted) return;
showDismissibleSnackBar(
context,
content: Text(context.l10n.channels_channelUpdateFailed('$e')),
content: Text(
context.l10n.channels_channelUpdateFailed('$e'),
),
);
}
},
+8 -2
View File
@@ -1218,8 +1218,14 @@ class _ChatScreenState extends State<ChatScreen> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(context.l10n.chat_type, contact.typeLabel(context.l10n)),
_buildInfoRow(context.l10n.chat_path, contact.pathLabel(context.l10n)),
_buildInfoRow(
context.l10n.chat_type,
contact.typeLabel(context.l10n),
),
_buildInfoRow(
context.l10n.chat_path,
contact.pathLabel(context.l10n),
),
_buildInfoRow(
context.l10n.contact_lastSeen,
_formatContactLastMessage(contact.lastMessageAt),
+9 -12
View File
@@ -932,16 +932,15 @@ class _ContactsScreenState extends State<ContactsScreen>
_showRoomLogin(context, contact, RoomLoginDestination.chat);
} else {
final connector = context.read<MeshCoreConnector>();
final unread =
connector.getUnreadCountForContactKey(contact.publicKeyHex);
final unread = connector.getUnreadCountForContactKey(
contact.publicKeyHex,
);
connector.markContactRead(contact.publicKeyHex);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
contact: contact,
initialUnreadCount: unread,
),
builder: (context) =>
ChatScreen(contact: contact, initialUnreadCount: unread),
),
);
}
@@ -998,8 +997,9 @@ class _ContactsScreenState extends State<ContactsScreen>
room: room,
onLogin: (password, isAdmin) {
final connector = context.read<MeshCoreConnector>();
final unread =
connector.getUnreadCountForContactKey(room.publicKeyHex);
final unread = connector.getUnreadCountForContactKey(
room.publicKeyHex,
);
connector.markContactRead(room.publicKeyHex);
Navigator.push(
context,
@@ -1011,10 +1011,7 @@ class _ContactsScreenState extends State<ContactsScreen>
password: password,
isAdmin: isAdmin,
)
: ChatScreen(
contact: room,
initialUnreadCount: unread,
),
: ChatScreen(contact: room, initialUnreadCount: unread),
),
);
},
+99
View File
@@ -1,6 +1,7 @@
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
@@ -56,8 +57,11 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
static const double _maxAntennaFeet = 400.0;
static const double _maxAntennaMeters = _maxAntennaFeet / _metersToFeet;
static const double _labelZoomThreshold = 8.5;
static const double _mapMinZoom = 2.0;
static const double _mapMaxZoom = 18.0;
final LineOfSightService _lineOfSightService = LineOfSightService();
final MapController _mapController = MapController();
bool _loading = false;
String? _error;
@@ -99,10 +103,85 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
@override
void dispose() {
_mapController.dispose();
_lineOfSightService.dispose();
super.dispose();
}
bool _isDesktopPlatform(TargetPlatform platform) {
return platform == TargetPlatform.linux ||
platform == TargetPlatform.windows ||
platform == TargetPlatform.macOS;
}
void _zoomMapBy(double delta) {
final camera = _mapController.camera;
final nextZoom = (camera.zoom + delta)
.clamp(_mapMinZoom, _mapMaxZoom)
.toDouble();
_mapController.move(camera.center, nextZoom);
}
void _resetMapView({
required LatLng initialCenter,
required double initialZoom,
required LatLngBounds? bounds,
}) {
if (bounds != null) {
_mapController.fitCamera(
CameraFit.bounds(
bounds: bounds,
padding: const EdgeInsets.all(64),
maxZoom: 16,
),
);
return;
}
_mapController.move(initialCenter, initialZoom);
}
Widget _buildDesktopMapControls({
required LatLng initialCenter,
required double initialZoom,
required LatLngBounds? bounds,
}) {
final screenHeight = MediaQuery.of(context).size.height;
final topOffset = _showHud
? math.min(screenHeight * 0.52 + 24, screenHeight - 220)
: 12.0;
return Positioned(
top: topOffset,
left: 12,
child: Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
onPressed: () => _resetMapView(
initialCenter: initialCenter,
initialZoom: initialZoom,
bounds: bounds,
),
),
],
),
),
);
}
Future<void> _runLos() async {
final start = _start;
final end = _end;
@@ -325,6 +404,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
? LatLngBounds.fromPoints(mapPoints)
: null;
final initialZoom = mapPoints.length > 1 ? 13.0 : 2.0;
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
if (!_didReceivePositionUpdate) {
_showMarkerLabels = initialZoom >= _labelZoomThreshold;
}
@@ -350,6 +430,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
body: Stack(
children: [
FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter: initialCenter,
initialZoom: initialZoom,
@@ -362,7 +443,19 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
),
interactionOptions: InteractionOptions(
flags: ~InteractiveFlag.rotate,
scrollWheelVelocity: isDesktop ? 0.012 : 0.005,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled(),
keyboardOptions: isDesktop
? const KeyboardOptions(
enableArrowKeysPanning: true,
enableWASDPanning: true,
enableRFZooming: true,
)
: const KeyboardOptions.disabled(),
),
minZoom: _mapMinZoom,
maxZoom: _mapMaxZoom,
onLongPress: (_, point) => _addCustomPoint(point),
onPositionChanged: (camera, hasGesture) {
final shouldShow = camera.zoom >= _labelZoomThreshold;
@@ -389,6 +482,12 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
),
],
),
if (isDesktop)
_buildDesktopMapControls(
initialCenter: initialCenter,
initialZoom: initialZoom,
bounds: bounds,
),
if (_showHud)
Positioned(
left: 12,
+78 -4
View File
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
@@ -18,6 +19,9 @@ class MapCacheScreen extends StatefulWidget {
}
class _MapCacheScreenState extends State<MapCacheScreen> {
static const double _mapMinZoom = 2.0;
static const double _mapMaxZoom = 18.0;
final MapController _mapController = MapController();
LatLngBounds? _selectedBounds;
@@ -43,6 +47,61 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
super.dispose();
}
bool _isDesktopPlatform(TargetPlatform platform) {
return platform == TargetPlatform.linux ||
platform == TargetPlatform.windows ||
platform == TargetPlatform.macOS;
}
void _zoomMapBy(double delta) {
final camera = _mapController.camera;
final nextZoom = (camera.zoom + delta)
.clamp(_mapMinZoom, _mapMaxZoom)
.toDouble();
_mapController.move(camera.center, nextZoom);
}
void _resetMapView() {
final bounds = _selectedBounds;
if (bounds != null) {
_mapController.fitCamera(
CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(48)),
);
return;
}
_mapController.move(const LatLng(0, 0), 2.0);
}
Widget _buildDesktopMapControls() {
return Positioned(
top: 12,
left: 12,
child: Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
onPressed: _resetMapView,
),
],
),
),
);
}
void _loadSettings() {
final settings = context.read<AppSettingsService>().settings;
final bounds = MapTileCacheService.boundsFromJson(settings.mapCacheBounds);
@@ -222,6 +281,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
final tileCache = context.read<MapTileCacheService>();
final selectedBounds = _selectedBounds;
final l10n = context.l10n;
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
final progressValue = _estimatedTiles == 0
? 0.0
: (_completedTiles / _estimatedTiles).clamp(0.0, 1.0).toDouble();
@@ -238,11 +298,24 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
children: [
FlutterMap(
mapController: _mapController,
options: const MapOptions(
initialCenter: LatLng(0, 0),
options: MapOptions(
initialCenter: const LatLng(0, 0),
initialZoom: 2.0,
minZoom: 2.0,
maxZoom: 18.0,
minZoom: _mapMinZoom,
maxZoom: _mapMaxZoom,
interactionOptions: InteractionOptions(
flags: ~InteractiveFlag.rotate,
scrollWheelVelocity: isDesktop ? 0.012 : 0.005,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled(),
keyboardOptions: isDesktop
? const KeyboardOptions(
enableArrowKeysPanning: true,
enableWASDPanning: true,
enableRFZooming: true,
)
: const KeyboardOptions.disabled(),
),
),
children: [
TileLayer(
@@ -265,6 +338,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
),
],
),
if (isDesktop) _buildDesktopMapControls(),
Positioned(
top: 12,
right: 12,
+80 -5
View File
@@ -1,6 +1,5 @@
import 'dart:collection';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -58,6 +57,8 @@ class MapScreen extends StatefulWidget {
class _MapScreenState extends State<MapScreen> {
// Zoom level at which node labels start to appear
static const double _labelZoomThreshold = 14.0;
static const double _mapMinZoom = 2.0;
static const double _mapMaxZoom = 18.0;
final MapController _mapController = MapController();
final MapMarkerService _markerService = MapMarkerService();
@@ -150,11 +151,62 @@ class _MapScreenState extends State<MapScreen> {
return zoom.clamp(4.0, 15.0);
}
bool _isDesktopPlatform(TargetPlatform platform) {
return platform == TargetPlatform.linux ||
platform == TargetPlatform.windows ||
platform == TargetPlatform.macOS;
}
void _zoomMapBy(double delta) {
final camera = _mapController.camera;
final nextZoom = (camera.zoom + delta)
.clamp(_mapMinZoom, _mapMaxZoom)
.toDouble();
_mapController.move(camera.center, nextZoom);
}
Widget _buildDesktopMapControls(
BuildContext context, {
required LatLng center,
required double zoom,
required bool hasPathSelector,
}) {
return Positioned(
left: 16,
top: hasPathSelector ? null : 16,
bottom: hasPathSelector ? 16 : null,
child: Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
onPressed: () => _mapController.move(center, zoom),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Consumer3<MeshCoreConnector, AppSettingsService, PathHistoryService>(
builder: (context, connector, settingsService, pathHistory, child) {
final tileCache = context.read<MapTileCacheService>();
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
final settings = settingsService.settings;
final allContacts = connector.allContacts;
@@ -451,10 +503,20 @@ class _MapScreenState extends State<MapScreen> {
options: MapOptions(
initialCenter: center,
initialZoom: initialZoom,
minZoom: 2.0,
maxZoom: 18.0,
minZoom: _mapMinZoom,
maxZoom: _mapMaxZoom,
interactionOptions: InteractionOptions(
flags: ~InteractiveFlag.rotate,
scrollWheelVelocity: isDesktop ? 0.012 : 0.005,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled(),
keyboardOptions: isDesktop
? const KeyboardOptions(
enableArrowKeysPanning: true,
enableWASDPanning: true,
enableRFZooming: true,
)
: const KeyboardOptions.disabled(),
),
onTap: (_, latLng) {
if (_isSelectingPoi) {
@@ -598,6 +660,13 @@ class _MapScreenState extends State<MapScreen> {
sharedMarkers.length,
guessedLocations.length,
),
if (isDesktop)
_buildDesktopMapControls(
context,
center: center,
zoom: initialZoom,
hasPathSelector: _isBuildingPathTrace,
),
if (_isBuildingPathTrace) _buildPathTraceOverlay(),
],
),
@@ -1480,8 +1549,14 @@ class _MapScreenState extends State<MapScreen> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(context.l10n.map_type, contact.typeLabel(context.l10n)),
_buildInfoRow(context.l10n.map_path, contact.pathLabel(context.l10n)),
_buildInfoRow(
context.l10n.map_type,
contact.typeLabel(context.l10n),
),
_buildInfoRow(
context.l10n.map_path,
contact.pathLabel(context.l10n),
),
if (contact.hasLocation)
_buildInfoRow(
context.l10n.map_location,
+85 -3
View File
@@ -76,9 +76,12 @@ class PathTraceMapScreen extends StatefulWidget {
class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
static const double _labelZoomThreshold = 8.5;
static const double _mapMinZoom = 2.0;
static const double _mapMaxZoom = 18.0;
//miles to meters conversion for filtering out repeaters that are too far from the last known GPS hop to be a likely match, to avoid false matches that throw off the inferred positions of other hops in the path
static const double _maxRepeaterMatchDistanceMeters = 40 * 1609.344;
final MapController _mapController = MapController();
StreamSubscription<Uint8List>? _frameSubscription;
Timer? _timeoutTimer;
@@ -116,11 +119,74 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
@override
void dispose() {
_mapController.dispose();
_frameSubscription?.cancel();
_timeoutTimer?.cancel();
super.dispose();
}
bool _isDesktopPlatform(TargetPlatform platform) {
return platform == TargetPlatform.linux ||
platform == TargetPlatform.windows ||
platform == TargetPlatform.macOS;
}
void _zoomMapBy(double delta) {
final camera = _mapController.camera;
final nextZoom = (camera.zoom + delta)
.clamp(_mapMinZoom, _mapMaxZoom)
.toDouble();
_mapController.move(camera.center, nextZoom);
}
void _resetMapView() {
final bounds = _bounds;
if (bounds != null) {
_mapController.fitCamera(
CameraFit.bounds(
bounds: bounds,
padding: const EdgeInsets.all(64),
maxZoom: 16,
),
);
return;
}
final center = _initialCenter;
if (center != null) {
_mapController.move(center, _initialZoom);
}
}
Widget _buildDesktopMapControls() {
return Positioned(
top: 16,
left: 16,
child: Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
onPressed: _resetMapView,
),
],
),
),
);
}
Uint8List buildPath(Uint8List pathBytes) {
Uint8List traceBytes;
@@ -519,6 +585,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
),
if (_hasData)
_buildMapPathTrace(context, tileCache, _targetContact),
if (_hasData && _isDesktopPlatform(defaultTargetPlatform))
_buildDesktopMapControls(),
if (_points.isEmpty &&
!_hasData &&
!_isLoading &&
@@ -801,10 +869,24 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
MapTileCacheService tileCache,
Contact? target,
) {
final isDesktop = _isDesktopPlatform(defaultTargetPlatform);
return FlutterMap(
key: _mapKey,
mapController: _mapController,
options: MapOptions(
interactionOptions: InteractionOptions(flags: ~InteractiveFlag.rotate),
interactionOptions: InteractionOptions(
flags: ~InteractiveFlag.rotate,
scrollWheelVelocity: isDesktop ? 0.012 : 0.005,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled(),
keyboardOptions: isDesktop
? const KeyboardOptions(
enableArrowKeysPanning: true,
enableWASDPanning: true,
enableRFZooming: true,
)
: const KeyboardOptions.disabled(),
),
initialCenter: _initialCenter!,
initialZoom: _initialZoom,
initialCameraFit: _bounds == null
@@ -814,8 +896,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
padding: const EdgeInsets.all(64),
maxZoom: 16,
),
minZoom: 2.0,
maxZoom: 18.0,
minZoom: _mapMinZoom,
maxZoom: _mapMaxZoom,
onPositionChanged: (camera, hasGesture) {
final shouldShow = camera.zoom >= _labelZoomThreshold;
if (shouldShow != _showNodeLabels && mounted) {
+1 -2
View File
@@ -65,8 +65,7 @@ class BackgroundService {
return AppLocalizations.delegate.load(overrideLocale);
}
}
final preferred =
WidgetsBinding.instance.platformDispatcher.locales;
final preferred = WidgetsBinding.instance.platformDispatcher.locales;
final match = basicLocaleListResolution(preferred, supported);
return AppLocalizations.delegate.load(match);
}
+4 -16
View File
@@ -36,10 +36,7 @@ void main() {
});
test('repeater', () {
expect(
_contact(type: advTypeRepeater).typeLabel(l10n),
'Repeater',
);
expect(_contact(type: advTypeRepeater).typeLabel(l10n), 'Repeater');
});
test('room', () {
@@ -57,24 +54,15 @@ void main() {
group('Contact.pathLabel (override)', () {
test('override < 0 -> flood (forced)', () {
expect(
_contact(pathOverride: -1).pathLabel(l10n),
'Flood (forced)',
);
expect(_contact(pathOverride: -1).pathLabel(l10n), 'Flood (forced)');
});
test('override == 0 -> direct (forced)', () {
expect(
_contact(pathOverride: 0).pathLabel(l10n),
'Direct (forced)',
);
expect(_contact(pathOverride: 0).pathLabel(l10n), 'Direct (forced)');
});
test('override > 0 -> hops (forced)', () {
expect(
_contact(pathOverride: 3).pathLabel(l10n),
'3 hops (forced)',
);
expect(_contact(pathOverride: 3).pathLabel(l10n), '3 hops (forced)');
});
test('override takes precedence over pathLength', () {