mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Remove unused path_provider entry
This commit is contained in:
@@ -65,9 +65,6 @@ PODS:
|
|||||||
- ObjectBox (= 4.4.1)
|
- ObjectBox (= 4.4.1)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- record_ios (1.2.0):
|
- record_ios (1.2.0):
|
||||||
@@ -105,7 +102,6 @@ DEPENDENCIES:
|
|||||||
- nsd_ios (from `.symlinks/plugins/nsd_ios/ios`)
|
- nsd_ios (from `.symlinks/plugins/nsd_ios/ios`)
|
||||||
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
|
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- record_ios (from `.symlinks/plugins/record_ios/ios`)
|
- record_ios (from `.symlinks/plugins/record_ios/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
@@ -153,8 +149,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/objectbox_flutter_libs/ios"
|
:path: ".symlinks/plugins/objectbox_flutter_libs/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
record_ios:
|
record_ios:
|
||||||
@@ -182,14 +176,13 @@ SPEC CHECKSUMS:
|
|||||||
flutter_background_service_ios: 00d31bdff7b4bfe06d32375df358abe0329cf87e
|
flutter_background_service_ios: 00d31bdff7b4bfe06d32375df358abe0329cf87e
|
||||||
flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
|
flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
|
||||||
flutter_compass: b236ab69b61545cce89fd58527f401a7587d5cc1
|
flutter_compass: b236ab69b61545cce89fd58527f401a7587d5cc1
|
||||||
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
flutter_local_notifications: 643a3eda1ce1c0599413ca31672536d423dee214
|
||||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||||
nsd_ios: 596ad79109ddd3e52d665f650f36428049e3653e
|
nsd_ios: 596ad79109ddd3e52d665f650f36428049e3653e
|
||||||
ObjectBox: 7da4aceb5013d041bfafdbc6d744a26918b09757
|
ObjectBox: 7da4aceb5013d041bfafdbc6d744a26918b09757
|
||||||
objectbox_flutter_libs: 09b1dec1b4cd27bf1a5f9bae7ccaa7e43588bf31
|
objectbox_flutter_libs: 09b1dec1b4cd27bf1a5f9bae7ccaa7e43588bf31
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
record_ios: 412daca2350b228e698fffcd08f1f94ceb1e3844
|
record_ios: 412daca2350b228e698fffcd08f1f94ceb1e3844
|
||||||
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
|
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
|
||||||
|
|||||||
@@ -489,7 +489,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -511,7 +511,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -530,7 +530,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -547,7 +547,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
@@ -679,7 +679,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -702,7 +702,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 100;
|
CURRENT_PROJECT_VERSION = 101;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>100</string>
|
<string>101</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000199">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000228">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.436347">
|
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.473911">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: build_app" time="49.579697">
|
<testcase classname="fastlane.lanes" name="2: build_app" time="95.078238">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -231,10 +231,10 @@ extension ContactLocalization on Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get routeSummary {
|
String get routeSummary {
|
||||||
if (routeIsUnknown || !routeHasPath) {
|
if (routeIsUnknown) {
|
||||||
return 'Flood/Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
if (routeHopCount == 0) {
|
if (!routeHasPath || routeHopCount == 0) {
|
||||||
return 'Direct';
|
return 'Direct';
|
||||||
}
|
}
|
||||||
return '$routeHopCount hop${routeHopCount == 1 ? '' : 's'} via $routeHashSize-byte hashes';
|
return '$routeHopCount hop${routeHopCount == 1 ? '' : 's'} via $routeHashSize-byte hashes';
|
||||||
|
|||||||
@@ -2015,6 +2015,7 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
_error = null;
|
||||||
await _activeService.resetPath(contactPublicKey);
|
await _activeService.resetPath(contactPublicKey);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_error = 'Failed to reset path: $e';
|
_error = 'Failed to reset path: $e';
|
||||||
@@ -2034,6 +2035,7 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
_error = null;
|
||||||
final updatedContact = contact.copyWith(
|
final updatedContact = contact.copyWith(
|
||||||
outPathLen: signedEncodedPathLen,
|
outPathLen: signedEncodedPathLen,
|
||||||
outPath: Uint8List.fromList(paddedPathBytes),
|
outPath: Uint8List.fromList(paddedPathBytes),
|
||||||
|
|||||||
@@ -715,36 +715,33 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Settings cog - hidden in simple mode
|
const SizedBox(width: 8),
|
||||||
if (!context.watch<AppProvider>().isSimpleMode) ...[
|
GestureDetector(
|
||||||
const SizedBox(width: 8),
|
onTap: () {
|
||||||
GestureDetector(
|
Navigator.push(
|
||||||
onTap: () {
|
context,
|
||||||
Navigator.push(
|
MaterialPageRoute(
|
||||||
context,
|
builder: (context) => const DeviceConfigScreen(),
|
||||||
MaterialPageRoute(
|
),
|
||||||
builder: (context) => const DeviceConfigScreen(),
|
);
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => PacketLogScreen(
|
||||||
|
bleService: provider.bleService,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
onLongPress: () {
|
},
|
||||||
Navigator.push(
|
child: Container(
|
||||||
context,
|
width: 32,
|
||||||
MaterialPageRoute(
|
height: 32,
|
||||||
builder: (context) => PacketLogScreen(
|
alignment: Alignment.center,
|
||||||
bleService: provider.bleService,
|
child: const Icon(Icons.settings, size: 18),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: const Icon(Icons.settings, size: 18),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class MapTab extends StatefulWidget {
|
|||||||
|
|
||||||
class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||||
final MapController _mapController = MapController();
|
final MapController _mapController = MapController();
|
||||||
final TileCacheService _tileCache = TileCacheService();
|
late final TileCacheService _tileCache;
|
||||||
// DO NOT create a new LocationTrackingService instance here
|
// DO NOT create a new LocationTrackingService instance here
|
||||||
// Use the singleton from AppProvider instead via _locationService getter
|
// Use the singleton from AppProvider instead via _locationService getter
|
||||||
final MapMarkerService _markerService = MapMarkerService();
|
final MapMarkerService _markerService = MapMarkerService();
|
||||||
@@ -117,6 +117,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_tileCache = context.read<TileCacheService>();
|
||||||
// Initialize Slovenian WMS layers with CRS
|
// Initialize Slovenian WMS layers with CRS
|
||||||
_slovenianAerialLayer = MapLayer.getSlovenianAerial2024(slovenianCrs);
|
_slovenianAerialLayer = MapLayer.getSlovenianAerial2024(slovenianCrs);
|
||||||
_dtk25Layer = MapLayer.getDTK25(slovenianCrs);
|
_dtk25Layer = MapLayer.getDTK25(slovenianCrs);
|
||||||
@@ -446,7 +447,6 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
|||||||
// Restore the original callback instead of setting to null
|
// Restore the original callback instead of setting to null
|
||||||
_locationService.onPositionUpdate = _originalLocationCallback;
|
_locationService.onPositionUpdate = _originalLocationCallback;
|
||||||
_mapController.dispose();
|
_mapController.dispose();
|
||||||
_tileCache.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,47 +10,66 @@ class TileCacheService {
|
|||||||
|
|
||||||
// Global flag to ensure ObjectBox is only initialized once
|
// Global flag to ensure ObjectBox is only initialized once
|
||||||
static bool _objectBoxInitialized = false;
|
static bool _objectBoxInitialized = false;
|
||||||
static final _initLock = <String, Future<void>>{};
|
static Future<void>? _objectBoxInitialization;
|
||||||
|
|
||||||
late final FMTCStore _store;
|
Future<void>? _initializeFuture;
|
||||||
|
FMTCStore? _store;
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
bool _isDownloading = false;
|
bool _isDownloading = false;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
if (_isInitialized) return;
|
if (_isInitialized) return;
|
||||||
|
_initializeFuture ??= _initializeInternal();
|
||||||
|
await _initializeFuture;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure we only initialize ObjectBox once globally
|
Future<void> _initializeInternal() async {
|
||||||
if (!_objectBoxInitialized) {
|
|
||||||
// Use a lock to prevent concurrent initialization attempts
|
|
||||||
final initFuture = _initLock.putIfAbsent('objectbox', () async {
|
|
||||||
try {
|
|
||||||
await FMTCObjectBoxBackend().initialise();
|
|
||||||
_objectBoxInitialized = true;
|
|
||||||
} catch (e) {
|
|
||||||
// Already initialized or error - that's okay
|
|
||||||
_objectBoxInitialized = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await initFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_store = FMTCStore(_storeName);
|
await _ensureObjectBoxInitialized();
|
||||||
await _store.manage.create();
|
|
||||||
_isInitialized = true;
|
final store = FMTCStore(_storeName);
|
||||||
} catch (e) {
|
try {
|
||||||
// Store might already exist
|
await store.manage.create();
|
||||||
_store = FMTCStore(_storeName);
|
} catch (_) {
|
||||||
|
// The store may already exist from a prior initialization.
|
||||||
|
}
|
||||||
|
|
||||||
|
_store = store;
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
|
} catch (_) {
|
||||||
|
_initializeFuture = null;
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FMTCTileProvider getTileProvider(MapLayer layer) {
|
Future<void> _ensureObjectBoxInitialized() async {
|
||||||
if (!_isInitialized) {
|
if (_objectBoxInitialized) return;
|
||||||
|
|
||||||
|
_objectBoxInitialization ??= () async {
|
||||||
|
try {
|
||||||
|
await FMTCObjectBoxBackend().initialise();
|
||||||
|
} catch (_) {
|
||||||
|
// Treat repeated backend initialization as a no-op.
|
||||||
|
} finally {
|
||||||
|
_objectBoxInitialized = true;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
await _objectBoxInitialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMTCStore _requireStore() {
|
||||||
|
final store = _store;
|
||||||
|
if (!_isInitialized || store == null) {
|
||||||
throw StateError(
|
throw StateError(
|
||||||
'TileCacheService not initialized. Call initialize() first.',
|
'TileCacheService not initialized. Call initialize() first.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileProvider getTileProvider(MapLayer layer) {
|
||||||
|
_requireStore();
|
||||||
return FMTCTileProvider(
|
return FMTCTileProvider(
|
||||||
stores: {_storeName: BrowseStoreStrategy.readUpdateCreate},
|
stores: {_storeName: BrowseStoreStrategy.readUpdateCreate},
|
||||||
loadingStrategy: BrowseLoadingStrategy.cacheFirst,
|
loadingStrategy: BrowseLoadingStrategy.cacheFirst,
|
||||||
@@ -60,12 +79,8 @@ class TileCacheService {
|
|||||||
|
|
||||||
/// Get tile provider for WMS layers with caching support
|
/// Get tile provider for WMS layers with caching support
|
||||||
/// WMS layers require special handling because they use WMSTileLayerOptions
|
/// WMS layers require special handling because they use WMSTileLayerOptions
|
||||||
FMTCTileProvider getTileProviderForWms(MapLayer layer) {
|
TileProvider getTileProviderForWms(MapLayer layer) {
|
||||||
if (!_isInitialized) {
|
_requireStore();
|
||||||
throw StateError(
|
|
||||||
'TileCacheService not initialized. Call initialize() first.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!layer.isWms) {
|
if (!layer.isWms) {
|
||||||
throw ArgumentError('Layer must be a WMS layer');
|
throw ArgumentError('Layer must be a WMS layer');
|
||||||
}
|
}
|
||||||
@@ -97,6 +112,7 @@ class TileCacheService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_isDownloading = true;
|
_isDownloading = true;
|
||||||
|
final store = _requireStore();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final region = RectangleRegion(bounds);
|
final region = RectangleRegion(bounds);
|
||||||
@@ -107,7 +123,7 @@ class TileCacheService {
|
|||||||
options: TileLayer(urlTemplate: layer.urlTemplate),
|
options: TileLayer(urlTemplate: layer.urlTemplate),
|
||||||
);
|
);
|
||||||
|
|
||||||
final download = _store.download.startForeground(region: downloadable);
|
final download = store.download.startForeground(region: downloadable);
|
||||||
|
|
||||||
await for (final progress in download.downloadProgress) {
|
await for (final progress in download.downloadProgress) {
|
||||||
if (onProgress != null && progress.maxTilesCount > 0) {
|
if (onProgress != null && progress.maxTilesCount > 0) {
|
||||||
@@ -127,24 +143,25 @@ class TileCacheService {
|
|||||||
|
|
||||||
Future<void> cancelDownload() async {
|
Future<void> cancelDownload() async {
|
||||||
if (!_isInitialized) return;
|
if (!_isInitialized) return;
|
||||||
await _store.download.cancel();
|
await _requireStore().download.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> clearCache() async {
|
Future<void> clearCache() async {
|
||||||
if (!_isInitialized) return;
|
if (!_isInitialized) return;
|
||||||
await _store.manage.delete();
|
final store = _requireStore();
|
||||||
await _store.manage.create();
|
await store.manage.delete();
|
||||||
|
await store.manage.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getCachedTileCount() async {
|
Future<int> getCachedTileCount() async {
|
||||||
if (!_isInitialized) return 0;
|
if (!_isInitialized) return 0;
|
||||||
final stats = await _store.stats.length;
|
final stats = await _requireStore().stats.length;
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> getCacheSizeMB() async {
|
Future<double> getCacheSizeMB() async {
|
||||||
if (!_isInitialized) return 0.0;
|
if (!_isInitialized) return 0.0;
|
||||||
final stats = await _store.stats.size;
|
final stats = await _requireStore().stats.size;
|
||||||
return stats / (1024 * 1024);
|
return stats / (1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +179,9 @@ class TileCacheService {
|
|||||||
Future<Map<String, dynamic>> getStoreStats() async {
|
Future<Map<String, dynamic>> getStoreStats() async {
|
||||||
if (!_isInitialized) return {};
|
if (!_isInitialized) return {};
|
||||||
|
|
||||||
final length = await _store.stats.length;
|
final store = _requireStore();
|
||||||
final size = await _store.stats.all.then((a) => a.size);
|
final length = await store.stats.length;
|
||||||
|
final size = await store.stats.all.then((a) => a.size);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'tileCount': length,
|
'tileCount': length,
|
||||||
|
|||||||
@@ -3,6 +3,18 @@ import 'package:flutter/material.dart';
|
|||||||
import '../../models/contact.dart';
|
import '../../models/contact.dart';
|
||||||
import '../../services/route_hash_preferences.dart';
|
import '../../services/route_hash_preferences.dart';
|
||||||
|
|
||||||
|
class ContactRouteDialogResult {
|
||||||
|
final ParsedContactRoute? route;
|
||||||
|
final bool shouldClear;
|
||||||
|
|
||||||
|
const ContactRouteDialogResult._({this.route, required this.shouldClear});
|
||||||
|
|
||||||
|
const ContactRouteDialogResult.set(ParsedContactRoute route)
|
||||||
|
: this._(route: route, shouldClear: false);
|
||||||
|
|
||||||
|
const ContactRouteDialogResult.clear() : this._(shouldClear: true);
|
||||||
|
}
|
||||||
|
|
||||||
class ContactRouteDialog extends StatefulWidget {
|
class ContactRouteDialog extends StatefulWidget {
|
||||||
final Contact contact;
|
final Contact contact;
|
||||||
final List<Contact> availableContacts;
|
final List<Contact> availableContacts;
|
||||||
@@ -13,12 +25,12 @@ class ContactRouteDialog extends StatefulWidget {
|
|||||||
required this.availableContacts,
|
required this.availableContacts,
|
||||||
});
|
});
|
||||||
|
|
||||||
static Future<ParsedContactRoute?> show(
|
static Future<ContactRouteDialogResult?> show(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required Contact contact,
|
required Contact contact,
|
||||||
required List<Contact> availableContacts,
|
required List<Contact> availableContacts,
|
||||||
}) {
|
}) {
|
||||||
return showModalBottomSheet<ParsedContactRoute>(
|
return showModalBottomSheet<ContactRouteDialogResult>(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
@@ -212,11 +224,20 @@ class _ContactRouteDialogState extends State<ContactRouteDialog> {
|
|||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Cancel'),
|
child: const Text('Cancel'),
|
||||||
),
|
),
|
||||||
|
if (widget.contact.routeHasPath)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(
|
||||||
|
context,
|
||||||
|
).pop(const ContactRouteDialogResult.clear()),
|
||||||
|
child: const Text('Clear Route'),
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: _parsedRoute == null
|
onPressed: _parsedRoute == null
|
||||||
? null
|
? null
|
||||||
: () => Navigator.of(context).pop(_parsedRoute),
|
: () => Navigator.of(
|
||||||
|
context,
|
||||||
|
).pop(ContactRouteDialogResult.set(_parsedRoute!)),
|
||||||
child: const Text('Set Route'),
|
child: const Text('Set Route'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../providers/map_provider.dart';
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import 'contact_route_dialog.dart';
|
import 'contact_route_dialog.dart';
|
||||||
import 'room_login_sheet.dart';
|
import 'room_login_sheet.dart';
|
||||||
|
import '../common/contact_avatar.dart';
|
||||||
import '../../utils/location_formats.dart';
|
import '../../utils/location_formats.dart';
|
||||||
import '../../utils/toast_logger.dart';
|
import '../../utils/toast_logger.dart';
|
||||||
import '../../utils/battery_display_helper.dart';
|
import '../../utils/battery_display_helper.dart';
|
||||||
@@ -147,54 +148,28 @@ class ContactTile extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
letterSpacing: -0.3,
|
letterSpacing: -0.3,
|
||||||
);
|
);
|
||||||
|
final timeAgoText = _getLocalizedTimeSinceLastSeen(context);
|
||||||
|
final timeAgoStyle = Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: contact.isRecentlySeen
|
||||||
|
? colorScheme.primary
|
||||||
|
: colorScheme.onSurfaceVariant,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
);
|
||||||
final subtitleWidget = isSimpleMode
|
final subtitleWidget = isSimpleMode
|
||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 2),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.access_time,
|
|
||||||
size: 12,
|
|
||||||
color: contact.isRecentlySeen ? Colors.green : Colors.grey,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'${AppLocalizations.of(context)!.lastSeen}: ${_getLocalizedTimeSinceLastSeen(context)}',
|
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (location != null) ...[
|
if (location != null) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 2),
|
||||||
Row(
|
_buildLocationLine(
|
||||||
children: [
|
context,
|
||||||
const Icon(Icons.location_on, size: 12, color: Colors.blue),
|
latitude: location.latitude,
|
||||||
const SizedBox(width: 4),
|
longitude: location.longitude,
|
||||||
Expanded(
|
distanceText: distanceText,
|
||||||
child: Text(
|
|
||||||
'GPS: ${location.latitude.toStringAsFixed(5)}, ${location.longitude.toStringAsFixed(5)}',
|
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (contact.type != ContactType.channel ||
|
if (contact.type != ContactType.channel) ...[
|
||||||
distanceText != null) ...[
|
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Wrap(
|
Row(children: [_buildRoutePill(context, contact)]),
|
||||||
spacing: 8,
|
|
||||||
runSpacing: 6,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (contact.type != ContactType.channel)
|
|
||||||
_buildRoutePill(context, contact),
|
|
||||||
if (distanceText != null)
|
|
||||||
_buildDistancePill(context, distanceText),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
] else
|
] else
|
||||||
Padding(
|
Padding(
|
||||||
@@ -285,40 +260,17 @@ class ContactTile extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
|
||||||
Icons.access_time,
|
|
||||||
size: 12,
|
|
||||||
color: contact.isRecentlySeen ? Colors.green : Colors.grey,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
_getLocalizedTimeSinceLastSeen(context),
|
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
|
||||||
),
|
|
||||||
if (location != null) ...[
|
if (location != null) ...[
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text('•', style: TextStyle(color: Colors.grey)),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (hasTelemetry)
|
|
||||||
const Icon(Icons.sensors, size: 12, color: Colors.green)
|
|
||||||
else
|
|
||||||
const Icon(
|
|
||||||
Icons.sensors_off,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: _buildLocationLine(
|
||||||
'GPS: ${location.latitude.toStringAsFixed(5)}, ${location.longitude.toStringAsFixed(5)}',
|
context,
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
latitude: location.latitude,
|
||||||
overflow: TextOverflow.ellipsis,
|
longitude: location.longitude,
|
||||||
|
distanceText: distanceText,
|
||||||
|
telemetryActive: hasTelemetry,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text('•', style: TextStyle(color: Colors.grey)),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Icon(Icons.sensors_off, size: 12, color: Colors.grey),
|
const Icon(Icons.sensors_off, size: 12, color: Colors.grey),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
@@ -328,20 +280,9 @@ class ContactTile extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (contact.type != ContactType.channel ||
|
if (contact.type != ContactType.channel) ...[
|
||||||
distanceText != null) ...[
|
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Wrap(
|
Row(children: [_buildRoutePill(context, contact)]),
|
||||||
spacing: 8,
|
|
||||||
runSpacing: 6,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (contact.type != ContactType.channel)
|
|
||||||
_buildRoutePill(context, contact),
|
|
||||||
if (distanceText != null)
|
|
||||||
_buildDistancePill(context, distanceText),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -378,51 +319,12 @@ class ContactTile extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
ContactAvatar(contact: contact, radius: 24),
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [
|
|
||||||
_getTypeColor(contact.type, context),
|
|
||||||
_getTypeColor(
|
|
||||||
contact.type,
|
|
||||||
context,
|
|
||||||
).withValues(alpha: 0.72),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: _getTypeColor(
|
|
||||||
contact.type,
|
|
||||||
context,
|
|
||||||
).withValues(alpha: 0.22),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: contact.roleEmoji != null
|
|
||||||
? Text(
|
|
||||||
contact.roleEmoji!,
|
|
||||||
style: const TextStyle(fontSize: 22),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
_getTypeIcon(contact.type),
|
|
||||||
color: Colors.white,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (contact.isNew)
|
if (contact.isNew)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: -2,
|
top: -2,
|
||||||
@@ -480,26 +382,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Container(
|
Text(timeAgoText, style: timeAgoStyle),
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 6,
|
|
||||||
vertical: 3,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorScheme.surfaceContainerHighest
|
|
||||||
.withValues(alpha: 0.75),
|
|
||||||
borderRadius: BorderRadius.circular(999),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
contact.type.displayName,
|
|
||||||
style: Theme.of(context).textTheme.labelSmall
|
|
||||||
?.copyWith(
|
|
||||||
color: colorScheme.onSurfaceVariant,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
letterSpacing: 0.1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!isSimpleMode && battery != null) ...[
|
if (!isSimpleMode && battery != null) ...[
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
_buildBatteryBadge(context, battery),
|
_buildBatteryBadge(context, battery),
|
||||||
@@ -656,18 +539,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
ContactAvatar(contact: contact, radius: 20),
|
||||||
backgroundColor: _getTypeColor(contact.type, context),
|
|
||||||
child: contact.roleEmoji != null
|
|
||||||
? Text(
|
|
||||||
contact.roleEmoji!,
|
|
||||||
style: const TextStyle(fontSize: 24),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
_getTypeIcon(contact.type),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -1116,17 +988,42 @@ class ContactTile extends StatelessWidget {
|
|||||||
.where((candidate) => candidate.publicKeyHex != contact.publicKeyHex)
|
.where((candidate) => candidate.publicKeyHex != contact.publicKeyHex)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final parsedRoute = await ContactRouteDialog.show(
|
final routeResult = await ContactRouteDialog.show(
|
||||||
context,
|
context,
|
||||||
contact: contact,
|
contact: contact,
|
||||||
availableContacts: availableContacts,
|
availableContacts: availableContacts,
|
||||||
);
|
);
|
||||||
if (parsedRoute == null || !context.mounted) {
|
if (routeResult == null || !context.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final previousSignedPathLen = contact.routeSignedPathLen;
|
final previousSignedPathLen = contact.routeSignedPathLen;
|
||||||
final previousPathBytes = Uint8List.fromList(contact.outPath);
|
final previousPathBytes = Uint8List.fromList(contact.outPath);
|
||||||
|
if (routeResult.shouldClear) {
|
||||||
|
contactsProvider.resetContactRouteLocal(contact.publicKey);
|
||||||
|
await connectionProvider.resetPath(contact.publicKey);
|
||||||
|
final error = connectionProvider.error;
|
||||||
|
if (error != null) {
|
||||||
|
contactsProvider.setContactRouteLocal(
|
||||||
|
contact.publicKey,
|
||||||
|
signedEncodedPathLen: previousSignedPathLen,
|
||||||
|
paddedPathBytes: previousPathBytes,
|
||||||
|
);
|
||||||
|
if (context.mounted) {
|
||||||
|
ToastLogger.error(context, 'Failed to clear route: $error');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(const SnackBar(content: Text('Route cleared')));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final parsedRoute = routeResult.route!;
|
||||||
contactsProvider.setContactRouteLocal(
|
contactsProvider.setContactRouteLocal(
|
||||||
contact.publicKey,
|
contact.publicKey,
|
||||||
signedEncodedPathLen: parsedRoute.signedEncodedPathLen,
|
signedEncodedPathLen: parsedRoute.signedEncodedPathLen,
|
||||||
@@ -1217,6 +1114,73 @@ class ContactTile extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildLocationMeta(
|
||||||
|
BuildContext context,
|
||||||
|
double latitude,
|
||||||
|
double longitude, {
|
||||||
|
bool telemetryActive = true,
|
||||||
|
}) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: telemetryActive
|
||||||
|
? colorScheme.primary.withValues(alpha: 0.12)
|
||||||
|
: colorScheme.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
telemetryActive
|
||||||
|
? Icons.navigation_rounded
|
||||||
|
: Icons.location_searching,
|
||||||
|
size: 11,
|
||||||
|
color: telemetryActive
|
||||||
|
? colorScheme.primary
|
||||||
|
: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
'${latitude.toStringAsFixed(5)}, ${longitude.toStringAsFixed(5)}',
|
||||||
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
letterSpacing: 0.1,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLocationLine(
|
||||||
|
BuildContext context, {
|
||||||
|
required double latitude,
|
||||||
|
required double longitude,
|
||||||
|
String? distanceText,
|
||||||
|
bool telemetryActive = true,
|
||||||
|
}) {
|
||||||
|
return Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 6,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildLocationMeta(
|
||||||
|
context,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
telemetryActive: telemetryActive,
|
||||||
|
),
|
||||||
|
if (distanceText != null) _buildDistancePill(context, distanceText),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert to Degrees Minutes Seconds (DMS) format
|
/// Convert to Degrees Minutes Seconds (DMS) format
|
||||||
String _convertToDMS(double lat, double lon) {
|
String _convertToDMS(double lat, double lon) {
|
||||||
String latDir = lat >= 0 ? 'N' : 'S';
|
String latDir = lat >= 0 ? 'N' : 'S';
|
||||||
@@ -1273,19 +1237,6 @@ class ContactTile extends StatelessWidget {
|
|||||||
return '$zone$letter (approximate)';
|
return '$zone$letter (approximate)';
|
||||||
}
|
}
|
||||||
|
|
||||||
IconData _getTypeIcon(ContactType type) {
|
|
||||||
switch (type) {
|
|
||||||
case ContactType.chat:
|
|
||||||
return Icons.person;
|
|
||||||
case ContactType.repeater:
|
|
||||||
return Icons.router;
|
|
||||||
case ContactType.room:
|
|
||||||
return Icons.tag;
|
|
||||||
default:
|
|
||||||
return Icons.help;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Color _getTypeColor(ContactType type, BuildContext context) {
|
Color _getTypeColor(ContactType type, BuildContext context) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ContactType.chat:
|
case ContactType.chat:
|
||||||
@@ -1411,6 +1362,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildRoutePill(BuildContext context, Contact contact) {
|
Widget _buildRoutePill(BuildContext context, Contact contact) {
|
||||||
final hasPath = contact.routeHasPath;
|
final hasPath = contact.routeHasPath;
|
||||||
|
final isDirect = !hasPath || contact.routeHopCount <= 0;
|
||||||
final scheme = Theme.of(context).colorScheme;
|
final scheme = Theme.of(context).colorScheme;
|
||||||
final textColor = Theme.of(
|
final textColor = Theme.of(
|
||||||
context,
|
context,
|
||||||
@@ -1418,9 +1370,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
final iconColor = Theme.of(
|
final iconColor = Theme.of(
|
||||||
context,
|
context,
|
||||||
).textTheme.labelSmall?.color?.withValues(alpha: 0.7);
|
).textTheme.labelSmall?.color?.withValues(alpha: 0.7);
|
||||||
final label = !hasPath
|
final label = isDirect
|
||||||
? AppLocalizations.of(context)!.flood
|
|
||||||
: contact.routeHopCount <= 0
|
|
||||||
? AppLocalizations.of(context)!.direct
|
? AppLocalizations.of(context)!.direct
|
||||||
: contact.routeCanonicalText;
|
: contact.routeCanonicalText;
|
||||||
|
|
||||||
@@ -1434,7 +1384,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
hasPath ? Icons.alt_route : Icons.waves,
|
isDirect ? Icons.north_east_rounded : Icons.alt_route,
|
||||||
size: 11,
|
size: 11,
|
||||||
color: iconColor,
|
color: iconColor,
|
||||||
),
|
),
|
||||||
@@ -1447,9 +1397,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
color: textColor,
|
color: textColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontFamily: hasPath && contact.routeHopCount > 0
|
fontFamily: !isDirect ? 'monospace' : null,
|
||||||
? 'monospace'
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1479,7 +1427,7 @@ class ContactTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
'${AppLocalizations.of(context)!.distance}: $distanceText',
|
distanceText,
|
||||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
|
|||||||
Reference in New Issue
Block a user