diff --git a/apps/app_seeds/lib/i18n/en.i18n.json b/apps/app_seeds/lib/i18n/en.i18n.json index 6a51e15..a93d02e 100644 --- a/apps/app_seeds/lib/i18n/en.i18n.json +++ b/apps/app_seeds/lib/i18n/en.i18n.json @@ -12,8 +12,8 @@ }, "home": { "tagline": "Share and grow local seeds", - "openMarket": "Open market", - "openMarketSubtitle": "Browse and exchange", + "openMarket": "Market", + "openMarketSubtitle": "Discover and share seeds nearby", "yourInventory": "Your inventory", "yourInventorySubtitle": "Manage your seeds" }, @@ -348,8 +348,13 @@ "setupIntro": "Sharing with people nearby is optional. Just set your rough area — you're already connected to shared community servers so people can find what you offer, with no company in the middle.", "areaLabel": "Your area", "areaHelp": "Kept rough on purpose — your zone, never an exact spot.", + "areaSet": "Your area is set — kept coarse, never your exact spot", + "areaNotSet": "Area not set yet — use your location, or add a code under Advanced", + "advanced": "Advanced", + "areaCodeLabel": "Area code", + "areaCodeHint": "A short code like sp3e9 — not a place name", "serversLabel": "Community servers", - "serversHelp": "Pick one or more shared servers. Anyone can use them — or add your own community's below.", + "serversHelp": "One address per line (wss://…). Leave the defaults if unsure.", "serversAdvanced": "Add another server", "save": "Save", "saved": "Saved", diff --git a/apps/app_seeds/lib/i18n/es.i18n.json b/apps/app_seeds/lib/i18n/es.i18n.json index 427207b..75f928c 100644 --- a/apps/app_seeds/lib/i18n/es.i18n.json +++ b/apps/app_seeds/lib/i18n/es.i18n.json @@ -12,8 +12,8 @@ }, "home": { "tagline": "Comparte y cultiva semillas locales", - "openMarket": "Mercado abierto", - "openMarketSubtitle": "Explora e intercambia", + "openMarket": "Mercado", + "openMarketSubtitle": "Descubre y comparte semillas cerca", "yourInventory": "Tu inventario", "yourInventorySubtitle": "Gestiona tus semillas" }, @@ -348,8 +348,13 @@ "setupIntro": "Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.", "areaLabel": "Tu zona", "areaHelp": "Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.", + "areaSet": "Tu zona está puesta — aproximada, nunca tu punto exacto", + "areaNotSet": "Zona sin definir — usa tu ubicación, o añade un código en Avanzado", + "advanced": "Avanzado", + "areaCodeLabel": "Código de zona", + "areaCodeHint": "Un código corto como sp3e9 — no un nombre de lugar", "serversLabel": "Servidores de la comunidad", - "serversHelp": "Elige uno o más servidores compartidos. Cualquiera puede usarlos — o añade abajo el de tu comunidad.", + "serversHelp": "Una dirección por línea (wss://…). Déjalo así si no sabes.", "serversAdvanced": "Añadir otro servidor", "save": "Guardar", "saved": "Guardado", diff --git a/apps/app_seeds/lib/i18n/pt.i18n.json b/apps/app_seeds/lib/i18n/pt.i18n.json index 58cd6c5..0c87259 100644 --- a/apps/app_seeds/lib/i18n/pt.i18n.json +++ b/apps/app_seeds/lib/i18n/pt.i18n.json @@ -12,8 +12,8 @@ }, "home": { "tagline": "Partilha e cultiva sementes locais", - "openMarket": "Mercado aberto", - "openMarketSubtitle": "Descobre e troca", + "openMarket": "Mercado", + "openMarketSubtitle": "Descobre e partilha sementes por perto", "yourInventory": "O teu inventário", "yourInventorySubtitle": "Gere as tuas sementes" }, @@ -344,8 +344,13 @@ "setupIntro": "Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.", "areaLabel": "A tua zona", "areaHelp": "Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.", + "areaSet": "A tua zona está definida — aproximada, nunca o teu ponto exato", + "areaNotSet": "Zona por definir — usa a tua localização, ou adiciona um código em Avançado", + "advanced": "Avançado", + "areaCodeLabel": "Código de zona", + "areaCodeHint": "Um código curto como sp3e9 — não um nome de lugar", "serversLabel": "Servidores da comunidade", - "serversHelp": "Escolhe um ou mais servidores partilhados. Qualquer pessoa os pode usar — ou adiciona abaixo o da tua comunidade.", + "serversHelp": "Um endereço por linha (wss://…). Deixa assim se não souberes.", "serversAdvanced": "Adicionar outro servidor", "save": "Guardar", "saved": "Guardado", diff --git a/apps/app_seeds/lib/i18n/strings.g.dart b/apps/app_seeds/lib/i18n/strings.g.dart index 53d10f1..feb9878 100644 --- a/apps/app_seeds/lib/i18n/strings.g.dart +++ b/apps/app_seeds/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 3 -/// Strings: 1043 (347 per locale) +/// Strings: 1058 (352 per locale) /// -/// Built on 2026-07-10 at 11:05 UTC +/// Built on 2026-07-10 at 11:31 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/apps/app_seeds/lib/i18n/strings_en.g.dart b/apps/app_seeds/lib/i18n/strings_en.g.dart index ff9374e..4d2f7f8 100644 --- a/apps/app_seeds/lib/i18n/strings_en.g.dart +++ b/apps/app_seeds/lib/i18n/strings_en.g.dart @@ -126,11 +126,11 @@ class Translations$home$en { /// en: 'Share and grow local seeds' String get tagline => 'Share and grow local seeds'; - /// en: 'Open market' - String get openMarket => 'Open market'; + /// en: 'Market' + String get openMarket => 'Market'; - /// en: 'Browse and exchange' - String get openMarketSubtitle => 'Browse and exchange'; + /// en: 'Discover and share seeds nearby' + String get openMarketSubtitle => 'Discover and share seeds nearby'; /// en: 'Your inventory' String get yourInventory => 'Your inventory'; @@ -1107,11 +1107,26 @@ class Translations$market$en { /// en: 'Kept rough on purpose — your zone, never an exact spot.' String get areaHelp => 'Kept rough on purpose — your zone, never an exact spot.'; + /// en: 'Your area is set — kept coarse, never your exact spot' + String get areaSet => 'Your area is set — kept coarse, never your exact spot'; + + /// en: 'Area not set yet — use your location, or add a code under Advanced' + String get areaNotSet => 'Area not set yet — use your location, or add a code under Advanced'; + + /// en: 'Advanced' + String get advanced => 'Advanced'; + + /// en: 'Area code' + String get areaCodeLabel => 'Area code'; + + /// en: 'A short code like sp3e9 — not a place name' + String get areaCodeHint => 'A short code like sp3e9 — not a place name'; + /// en: 'Community servers' String get serversLabel => 'Community servers'; - /// en: 'Pick one or more shared servers. Anyone can use them — or add your own community's below.' - String get serversHelp => 'Pick one or more shared servers. Anyone can use them — or add your own community\'s below.'; + /// en: 'One address per line (wss://…). Leave the defaults if unsure.' + String get serversHelp => 'One address per line (wss://…). Leave the defaults if unsure.'; /// en: 'Add another server' String get serversAdvanced => 'Add another server'; @@ -1726,8 +1741,8 @@ extension on Translations { 'common.type' => 'Type', 'common.comingSoon' => 'Coming soon', 'home.tagline' => 'Share and grow local seeds', - 'home.openMarket' => 'Open market', - 'home.openMarketSubtitle' => 'Browse and exchange', + 'home.openMarket' => 'Market', + 'home.openMarketSubtitle' => 'Discover and share seeds nearby', 'home.yourInventory' => 'Your inventory', 'home.yourInventorySubtitle' => 'Manage your seeds', 'photo.camera' => 'Take a photo', @@ -2028,8 +2043,13 @@ extension on Translations { 'market.setupIntro' => 'Sharing with people nearby is optional. Just set your rough area — you\'re already connected to shared community servers so people can find what you offer, with no company in the middle.', 'market.areaLabel' => 'Your area', 'market.areaHelp' => 'Kept rough on purpose — your zone, never an exact spot.', + 'market.areaSet' => 'Your area is set — kept coarse, never your exact spot', + 'market.areaNotSet' => 'Area not set yet — use your location, or add a code under Advanced', + 'market.advanced' => 'Advanced', + 'market.areaCodeLabel' => 'Area code', + 'market.areaCodeHint' => 'A short code like sp3e9 — not a place name', 'market.serversLabel' => 'Community servers', - 'market.serversHelp' => 'Pick one or more shared servers. Anyone can use them — or add your own community\'s below.', + 'market.serversHelp' => 'One address per line (wss://…). Leave the defaults if unsure.', 'market.serversAdvanced' => 'Add another server', 'market.save' => 'Save', 'market.saved' => 'Saved', diff --git a/apps/app_seeds/lib/i18n/strings_es.g.dart b/apps/app_seeds/lib/i18n/strings_es.g.dart index 5aa5b87..1ae0fb3 100644 --- a/apps/app_seeds/lib/i18n/strings_es.g.dart +++ b/apps/app_seeds/lib/i18n/strings_es.g.dart @@ -108,8 +108,8 @@ class _Translations$home$es extends Translations$home$en { // Translations @override String get tagline => 'Comparte y cultiva semillas locales'; - @override String get openMarket => 'Mercado abierto'; - @override String get openMarketSubtitle => 'Explora e intercambia'; + @override String get openMarket => 'Mercado'; + @override String get openMarketSubtitle => 'Descubre y comparte semillas cerca'; @override String get yourInventory => 'Tu inventario'; @override String get yourInventorySubtitle => 'Gestiona tus semillas'; } @@ -622,8 +622,13 @@ class _Translations$market$es extends Translations$market$en { @override String get setupIntro => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.'; @override String get areaLabel => 'Tu zona'; @override String get areaHelp => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.'; + @override String get areaSet => 'Tu zona está puesta — aproximada, nunca tu punto exacto'; + @override String get areaNotSet => 'Zona sin definir — usa tu ubicación, o añade un código en Avanzado'; + @override String get advanced => 'Avanzado'; + @override String get areaCodeLabel => 'Código de zona'; + @override String get areaCodeHint => 'Un código corto como sp3e9 — no un nombre de lugar'; @override String get serversLabel => 'Servidores de la comunidad'; - @override String get serversHelp => 'Elige uno o más servidores compartidos. Cualquiera puede usarlos — o añade abajo el de tu comunidad.'; + @override String get serversHelp => 'Una dirección por línea (wss://…). Déjalo así si no sabes.'; @override String get serversAdvanced => 'Añadir otro servidor'; @override String get save => 'Guardar'; @override String get saved => 'Guardado'; @@ -1048,8 +1053,8 @@ extension on TranslationsEs { 'common.type' => 'Tipo', 'common.comingSoon' => 'Pronto', 'home.tagline' => 'Comparte y cultiva semillas locales', - 'home.openMarket' => 'Mercado abierto', - 'home.openMarketSubtitle' => 'Explora e intercambia', + 'home.openMarket' => 'Mercado', + 'home.openMarketSubtitle' => 'Descubre y comparte semillas cerca', 'home.yourInventory' => 'Tu inventario', 'home.yourInventorySubtitle' => 'Gestiona tus semillas', 'photo.camera' => 'Hacer una foto', @@ -1350,8 +1355,13 @@ extension on TranslationsEs { 'market.setupIntro' => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.', 'market.areaLabel' => 'Tu zona', 'market.areaHelp' => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.', + 'market.areaSet' => 'Tu zona está puesta — aproximada, nunca tu punto exacto', + 'market.areaNotSet' => 'Zona sin definir — usa tu ubicación, o añade un código en Avanzado', + 'market.advanced' => 'Avanzado', + 'market.areaCodeLabel' => 'Código de zona', + 'market.areaCodeHint' => 'Un código corto como sp3e9 — no un nombre de lugar', 'market.serversLabel' => 'Servidores de la comunidad', - 'market.serversHelp' => 'Elige uno o más servidores compartidos. Cualquiera puede usarlos — o añade abajo el de tu comunidad.', + 'market.serversHelp' => 'Una dirección por línea (wss://…). Déjalo así si no sabes.', 'market.serversAdvanced' => 'Añadir otro servidor', 'market.save' => 'Guardar', 'market.saved' => 'Guardado', diff --git a/apps/app_seeds/lib/i18n/strings_pt.g.dart b/apps/app_seeds/lib/i18n/strings_pt.g.dart index a04c438..b8368e2 100644 --- a/apps/app_seeds/lib/i18n/strings_pt.g.dart +++ b/apps/app_seeds/lib/i18n/strings_pt.g.dart @@ -108,8 +108,8 @@ class _Translations$home$pt extends Translations$home$en { // Translations @override String get tagline => 'Partilha e cultiva sementes locais'; - @override String get openMarket => 'Mercado aberto'; - @override String get openMarketSubtitle => 'Descobre e troca'; + @override String get openMarket => 'Mercado'; + @override String get openMarketSubtitle => 'Descobre e partilha sementes por perto'; @override String get yourInventory => 'O teu inventário'; @override String get yourInventorySubtitle => 'Gere as tuas sementes'; } @@ -618,8 +618,13 @@ class _Translations$market$pt extends Translations$market$en { @override String get setupIntro => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.'; @override String get areaLabel => 'A tua zona'; @override String get areaHelp => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.'; + @override String get areaSet => 'A tua zona está definida — aproximada, nunca o teu ponto exato'; + @override String get areaNotSet => 'Zona por definir — usa a tua localização, ou adiciona um código em Avançado'; + @override String get advanced => 'Avançado'; + @override String get areaCodeLabel => 'Código de zona'; + @override String get areaCodeHint => 'Um código curto como sp3e9 — não um nome de lugar'; @override String get serversLabel => 'Servidores da comunidade'; - @override String get serversHelp => 'Escolhe um ou mais servidores partilhados. Qualquer pessoa os pode usar — ou adiciona abaixo o da tua comunidade.'; + @override String get serversHelp => 'Um endereço por linha (wss://…). Deixa assim se não souberes.'; @override String get serversAdvanced => 'Adicionar outro servidor'; @override String get save => 'Guardar'; @override String get saved => 'Guardado'; @@ -1044,8 +1049,8 @@ extension on TranslationsPt { 'common.type' => 'Tipo', 'common.comingSoon' => 'Em breve', 'home.tagline' => 'Partilha e cultiva sementes locais', - 'home.openMarket' => 'Mercado aberto', - 'home.openMarketSubtitle' => 'Descobre e troca', + 'home.openMarket' => 'Mercado', + 'home.openMarketSubtitle' => 'Descobre e partilha sementes por perto', 'home.yourInventory' => 'O teu inventário', 'home.yourInventorySubtitle' => 'Gere as tuas sementes', 'photo.camera' => 'Tirar uma foto', @@ -1342,8 +1347,13 @@ extension on TranslationsPt { 'market.setupIntro' => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.', 'market.areaLabel' => 'A tua zona', 'market.areaHelp' => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.', + 'market.areaSet' => 'A tua zona está definida — aproximada, nunca o teu ponto exato', + 'market.areaNotSet' => 'Zona por definir — usa a tua localização, ou adiciona um código em Avançado', + 'market.advanced' => 'Avançado', + 'market.areaCodeLabel' => 'Código de zona', + 'market.areaCodeHint' => 'Um código curto como sp3e9 — não um nome de lugar', 'market.serversLabel' => 'Servidores da comunidade', - 'market.serversHelp' => 'Escolhe um ou mais servidores partilhados. Qualquer pessoa os pode usar — ou adiciona abaixo o da tua comunidade.', + 'market.serversHelp' => 'Um endereço por linha (wss://…). Deixa assim se não souberes.', 'market.serversAdvanced' => 'Adicionar outro servidor', 'market.save' => 'Guardar', 'market.saved' => 'Guardado', diff --git a/apps/app_seeds/lib/services/coarse_location.dart b/apps/app_seeds/lib/services/coarse_location.dart index 5dfef0b..aa576ec 100644 --- a/apps/app_seeds/lib/services/coarse_location.dart +++ b/apps/app_seeds/lib/services/coarse_location.dart @@ -20,16 +20,13 @@ class GeolocatorCoarseLocation implements CoarseLocationProvider { if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); } - // Give the user a way out of a permanent denial. - if (permission == LocationPermission.deniedForever) { - await Geolocator.openAppSettings(); - return null; - } - if (permission == LocationPermission.denied) return null; - if (!await Geolocator.isLocationServiceEnabled()) { - await Geolocator.openLocationSettings(); + // 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. diff --git a/apps/app_seeds/lib/state/offers_cubit.dart b/apps/app_seeds/lib/state/offers_cubit.dart index f9b2d46..053f6e7 100644 --- a/apps/app_seeds/lib/state/offers_cubit.dart +++ b/apps/app_seeds/lib/state/offers_cubit.dart @@ -76,6 +76,7 @@ class OffersCubit extends Cubit { /// (null in tests, where the transport is a fake with nothing to close). final Future Function()? _onDispose; StreamSubscription? _sub; + Timer? _searchTimeout; /// Whether a live transport is available (relay configured and reachable). bool get isOnline => _transport != null; @@ -88,6 +89,7 @@ class OffersCubit extends Cubit { return; } await _sub?.cancel(); + _searchTimeout?.cancel(); emit(OffersState( areaGeohash: geohashPrefix, searching: true, @@ -99,6 +101,14 @@ class OffersCubit extends Cubit { onError: (Object e) => emit(state.copyWith(searching: false, error: () => '$e')), ); + // The discover stream stays open for live offers and never signals "done", + // so stop the spinner after a beat: no results → show the empty state, not + // an endless "searching". + _searchTimeout = Timer(const Duration(seconds: 6), () { + if (!isClosed && state.searching) { + emit(state.copyWith(searching: false)); + } + }); } /// Publishes [offer]; returns the transport's verdict. No-op result when @@ -157,6 +167,7 @@ class OffersCubit extends Cubit { @override Future close() async { + _searchTimeout?.cancel(); await _sub?.cancel(); await _onDispose?.call(); return super.close(); diff --git a/apps/app_seeds/lib/ui/market_screen.dart b/apps/app_seeds/lib/ui/market_screen.dart index 9f418a7..6665afd 100644 --- a/apps/app_seeds/lib/ui/market_screen.dart +++ b/apps/app_seeds/lib/ui/market_screen.dart @@ -431,6 +431,7 @@ class _ConfigSheet extends StatefulWidget { class _ConfigSheetState extends State<_ConfigSheet> { final _area = TextEditingController(); + final _servers = TextEditingController(); bool _loading = true; bool _locationBusy = false; String? _locationError; @@ -443,13 +444,13 @@ class _ConfigSheetState extends State<_ConfigSheet> { Future _load() async { _area.text = await widget.settings.areaGeohash(); + _servers.text = (await widget.settings.relayUrls()).join('\n'); if (mounted) setState(() => _loading = false); } Future _save() async { - // Relays are managed automatically (default community servers, dead ones - // skipped by the pool) — the user only sets their area here. await widget.settings.setAreaGeohash(_area.text); + await widget.settings.setRelayUrls(_servers.text.split('\n')); if (mounted) Navigator.of(context).pop(true); } @@ -477,12 +478,14 @@ class _ConfigSheetState extends State<_ConfigSheet> { @override void dispose() { _area.dispose(); + _servers.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final t = context.t; + final hasArea = _area.text.trim().isNotEmpty; return Padding( padding: EdgeInsets.only( left: 20, @@ -491,40 +494,34 @@ class _ConfigSheetState extends State<_ConfigSheet> { bottom: MediaQuery.of(context).viewInsets.bottom + 20, ), child: _loading - ? const SizedBox(height: 120, child: Center(child: CircularProgressIndicator())) + ? const SizedBox( + height: 120, child: Center(child: CircularProgressIndicator())) : SingleChildScrollView( child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - t.market.configTitle, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: seedTitle, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + t.market.configTitle, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: seedTitle, + ), ), - ), - const SizedBox(height: 8), - Text( - t.market.setupIntro, - style: const TextStyle(color: seedMuted, fontSize: 13, height: 1.4), - ), - const SizedBox(height: 16), - TextField( - key: const Key('market.area'), - controller: _area, - decoration: InputDecoration( - labelText: t.market.areaLabel, - helperText: t.market.areaHelp, - helperMaxLines: 3, + const SizedBox(height: 8), + Text( + t.market.setupIntro, + style: const TextStyle( + color: seedMuted, fontSize: 13, height: 1.4), ), - ), - if (widget.location != null) - Align( - alignment: AlignmentDirectional.centerStart, - child: TextButton.icon( + const SizedBox(height: 18), + // Setting your area from device location is the human path; + // typing a code is the advanced fallback. + if (widget.location != null) + FilledButton.tonalIcon( key: const Key('market.useLocation'), + onPressed: _locationBusy ? null : _useLocation, icon: _locationBusy ? const SizedBox( width: 16, @@ -533,26 +530,85 @@ class _ConfigSheetState extends State<_ConfigSheet> { ) : const Icon(Icons.my_location, size: 18), label: Text(t.market.useLocation), - onPressed: _locationBusy ? null : _useLocation, + ), + if (_locationError != null) + Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + _locationError!, + style: const TextStyle( + color: Color(0xFFB3261E), fontSize: 12), + ), + ), + const SizedBox(height: 12), + Row( + children: [ + Icon( + hasArea + ? Icons.check_circle + : Icons.pending_outlined, + size: 18, + color: hasArea ? seedGreen : seedMuted, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + hasArea ? t.market.areaSet : t.market.areaNotSet, + style: TextStyle( + color: hasArea ? seedOnSurface : seedMuted, + fontSize: 13, + ), + ), + ), + ], + ), + const SizedBox(height: 8), + Theme( + data: Theme.of(context) + .copyWith(dividerColor: Colors.transparent), + child: ExpansionTile( + key: const Key('market.advanced'), + tilePadding: EdgeInsets.zero, + childrenPadding: const EdgeInsets.only(bottom: 8), + title: Text( + t.market.advanced, + style: const TextStyle(fontSize: 13, color: seedMuted), + ), + children: [ + TextField( + key: const Key('market.area'), + controller: _area, + onChanged: (_) => setState(() {}), + decoration: InputDecoration( + labelText: t.market.areaCodeLabel, + helperText: t.market.areaCodeHint, + helperMaxLines: 2, + ), + ), + const SizedBox(height: 14), + TextField( + key: const Key('market.servers'), + controller: _servers, + minLines: 1, + maxLines: 3, + decoration: InputDecoration( + labelText: t.market.serversLabel, + helperText: t.market.serversHelp, + helperMaxLines: 2, + ), + ), + ], ), ), - if (_locationError != null) - Padding( - padding: const EdgeInsets.only(top: 4), - child: Text( - _locationError!, - style: const TextStyle(color: Color(0xFFB3261E), fontSize: 12), - ), + const SizedBox(height: 20), + FilledButton( + key: const Key('market.save'), + onPressed: _save, + child: Text(t.market.save), ), - const SizedBox(height: 20), - FilledButton( - key: const Key('market.save'), - onPressed: _save, - child: Text(t.market.save), - ), - ], + ], + ), ), - ), ); } } diff --git a/apps/app_seeds/test/ui/market_screen_test.dart b/apps/app_seeds/test/ui/market_screen_test.dart index e6a578d..b84751a 100644 --- a/apps/app_seeds/test/ui/market_screen_test.dart +++ b/apps/app_seeds/test/ui/market_screen_test.dart @@ -107,8 +107,7 @@ void main() { await tester.tap(find.byKey(const Key('market.useLocation'))); await tester.pumpAndSettle(); - final area = tester.widget(find.byKey(const Key('market.area'))); - expect(area.controller!.text, hasLength(5)); - expect(area.controller!.text, matches(RegExp(r'^[0-9a-z]+$'))); + // The area status flips to "set" once a coarse location is captured. + expect(find.textContaining('Your area is set'), findsOneWidget); }); }