From on-device feedback: - Area is no longer shown as a raw geohash 'name'. 'Use my location' is the primary action; a human status line says whether your area is set (coarse, never exact). The geohash code + the relay servers moved under 'Advanced' (code field labelled 'a code like sp3e9 — not a place name'), which also brings back node/relay selection that had been fully hidden. - Location denial no longer yanks you into system settings — returns quietly with an actionable inline message. - Discovery no longer spins forever when nobody's sharing nearby: a timeout resolves 'searching' to the empty state. - Home card 'Open market' -> 'Market' + 'Discover and share seeds nearby' (the word 'open' confused). i18n en/es/pt; analyzer clean; the use-my-location test asserts the status flip.
50 lines
2 KiB
Dart
50 lines
2 KiB
Dart
import 'package:geolocator/geolocator.dart';
|
|
|
|
/// Supplies the device's *approximate* location (or null when unavailable or
|
|
/// denied). Behind an interface so the UI is fakeable in tests and the concrete
|
|
/// platform plugin stays at the composition root. The caller immediately reduces
|
|
/// the result to a low-precision geohash — a precise fix never leaves the device.
|
|
abstract interface class CoarseLocationProvider {
|
|
Future<({double lat, double lon})?> currentCoarseLatLon();
|
|
}
|
|
|
|
/// [geolocator]-backed implementation. Requests low accuracy only, and returns
|
|
/// null on any denial/error rather than throwing, so the UI can degrade quietly.
|
|
class GeolocatorCoarseLocation implements CoarseLocationProvider {
|
|
const GeolocatorCoarseLocation();
|
|
|
|
@override
|
|
Future<({double lat, double lon})?> currentCoarseLatLon() async {
|
|
try {
|
|
var permission = await Geolocator.checkPermission();
|
|
if (permission == LocationPermission.denied) {
|
|
permission = await Geolocator.requestPermission();
|
|
}
|
|
// Denied (or turned off): return null quietly — the UI shows an actionable
|
|
// message. We don't yank the user into system settings.
|
|
if (permission == LocationPermission.denied ||
|
|
permission == LocationPermission.deniedForever) {
|
|
return null;
|
|
}
|
|
if (!await Geolocator.isLocationServiceEnabled()) return null;
|
|
|
|
// A fresh coarse fix can take a while (or never come indoors); fall back
|
|
// to the last known position so the button stays responsive.
|
|
Position? position;
|
|
try {
|
|
position = await Geolocator.getCurrentPosition(
|
|
locationSettings: const LocationSettings(
|
|
accuracy: LocationAccuracy.low,
|
|
timeLimit: Duration(seconds: 12),
|
|
),
|
|
);
|
|
} catch (_) {
|
|
position = await Geolocator.getLastKnownPosition();
|
|
}
|
|
if (position == null) return null;
|
|
return (lat: position.latitude, lon: position.longitude);
|
|
} catch (_) {
|
|
return null; // plugin unsupported (desktop), etc.
|
|
}
|
|
}
|
|
}
|