feat(block2): 'use my location' — set the sharing area without typing a code
Makes the coarse area human, per feedback that a typed code is jargon. - CoarseLocationProvider interface + geolocator-backed impl (BSD-3): low accuracy only, returns null on any denial/error (degrades quietly). Behind an interface so it's fakeable and the plugin stays at the composition root. - Config sheet: a 'Use my approximate location' button (shown only when a provider is wired) fills the area from the device position, reduced to a 5-char geohash via commons_core's Geohash — a precise fix never leaves the device. 'Couldn't get your location' on failure. - Threaded via TaneApp(location); main wires GeolocatorCoarseLocation. - Platform: ACCESS_COARSE_LOCATION (Android) + NSLocationWhenInUseUsageDescription (iOS), both scoped to the coarse-area use. - i18n en/es/pt. Widget test fills the area from a fake location. 8 tests green.
This commit is contained in:
parent
f8f73c4153
commit
492bc62025
15 changed files with 176 additions and 10 deletions
|
|
@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/i18n/strings.g.dart';
|
||||
import 'package:tane/services/coarse_location.dart';
|
||||
import 'package:tane/services/social_service.dart';
|
||||
import 'package:tane/services/social_settings.dart';
|
||||
import 'package:tane/state/offers_cubit.dart';
|
||||
|
|
@ -13,6 +14,16 @@ import 'package:tane/ui/market_screen.dart';
|
|||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
/// A location provider returning a fixed coarse position.
|
||||
class FakeLocation implements CoarseLocationProvider {
|
||||
FakeLocation(this.lat, this.lon);
|
||||
final double lat;
|
||||
final double lon;
|
||||
@override
|
||||
Future<({double lat, double lon})?> currentCoarseLatLon() async =>
|
||||
(lat: lat, lon: lon);
|
||||
}
|
||||
|
||||
/// In-memory offer transport: discover matches by geohash prefix.
|
||||
class FakeOfferTransport implements OfferTransport {
|
||||
final List<Offer> offers;
|
||||
|
|
@ -120,4 +131,40 @@ void main() {
|
|||
expect(find.text('Seeds near you'), findsOneWidget); // app bar title
|
||||
expect(find.text('Set up sharing'), findsOneWidget); // offline prompt
|
||||
});
|
||||
|
||||
testWidgets('"use my location" fills the area with a coarse geohash',
|
||||
(tester) async {
|
||||
final social = await SocialService.fromRootSeedHex('00' * 32);
|
||||
final settings = SocialSettings(InMemorySecretStore());
|
||||
LocaleSettings.setLocaleSync(AppLocale.en);
|
||||
|
||||
await tester.pumpWidget(
|
||||
TranslationProvider(
|
||||
child: MaterialApp(
|
||||
locale: AppLocale.en.flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
home: MarketScreen(
|
||||
social: social,
|
||||
settings: settings,
|
||||
location: FakeLocation(41.39, 2.16), // Barcelona-ish
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('market.config')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('market.useLocation')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final area = tester.widget<TextField>(find.byKey(const Key('market.area')));
|
||||
expect(area.controller!.text, hasLength(5));
|
||||
expect(area.controller!.text, matches(RegExp(r'^[0-9a-z]+$')));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue