feat(market): search + pull-to-refresh in the market list

Match the inventory patterns the market was missing:
- Search: OffersState gains a `query` + `visibleOffers` getter and
  OffersCubit.search() filters the already-discovered offers locally by
  summary (case-insensitive), never re-hitting the transport. The filter
  survives a refresh. Rounded search field mirrors inventory's.
- Pull-to-refresh: RefreshIndicator re-runs discovery for the current
  area (empty state included, so a swipe re-scans). Text filter kept.
- i18n: market.searchHint / market.noMatches (en/es/pt).

Note: the shared slang output (strings*.g.dart, incl. the generated
Asturian locale) is regenerated wholesale, so it also carries the
in-progress `ast` locale keys already present in the working tree.
This commit is contained in:
vjrj 2026-07-10 15:40:13 +02:00
parent bd18978aff
commit beb29915d8
11 changed files with 1614 additions and 21 deletions

View file

@ -40,6 +40,7 @@
"langEs": "Español",
"langEn": "English",
"langPt": "Português",
"langAst": "Asturianu",
"about": "About",
"aboutText": "Local-first, encrypted inventory for traditional seeds. AGPL-3.0.",
"aboutOpen": "About Tanemaki"
@ -346,6 +347,8 @@
"setAreaBody": "Tell the market roughly where you are to see seeds nearby.",
"searching": "Looking around your area…",
"empty": "No seeds shared near you yet",
"searchHint": "Search these seeds",
"noMatches": "No shared seeds match your search",
"near": "Near you",
"contact": "Message",
"mine": "You",

View file

@ -346,6 +346,8 @@
"setAreaBody": "Dile al mercado tu zona aproximada para ver semillas cerca.",
"searching": "Buscando por tu zona…",
"empty": "Aún no hay semillas compartidas cerca de ti",
"searchHint": "Buscar entre estas semillas",
"noMatches": "Ninguna semilla compartida coincide con tu búsqueda",
"near": "Cerca de ti",
"contact": "Mensaje",
"mine": "Tú",

View file

@ -40,6 +40,7 @@
"langEs": "Español",
"langEn": "English",
"langPt": "Português",
"langAst": "Asturianu",
"about": "Acerca de",
"aboutText": "Inventário local e cifrado para sementes tradicionais. AGPL-3.0.",
"aboutOpen": "Acerca do Tanemaki"
@ -342,6 +343,8 @@
"setAreaBody": "Diz ao mercado a tua zona aproximada para ver sementes por perto.",
"searching": "A procurar pela tua zona…",
"empty": "Ainda não há sementes partilhadas perto de ti",
"searchHint": "Procurar nestas sementes",
"noMatches": "Nenhuma semente partilhada corresponde à procura",
"near": "Perto de ti",
"contact": "Mensagem",
"mine": "Tu",

View file

@ -3,10 +3,10 @@
/// Source: lib/i18n
/// To regenerate, run: `dart run slang`
///
/// Locales: 3
/// Strings: 1073 (357 per locale)
/// Locales: 4
/// Strings: 1441 (360 per locale)
///
/// Built on 2026-07-10 at 11:35 UTC
/// Built on 2026-07-10 at 13:39 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
@ -18,6 +18,7 @@ import 'package:slang/generated.dart';
import 'package:slang_flutter/slang_flutter.dart';
export 'package:slang_flutter/slang_flutter.dart';
import 'strings_ast.g.dart' as l_ast;
import 'strings_es.g.dart' as l_es;
import 'strings_pt.g.dart' as l_pt;
part 'strings_en.g.dart';
@ -30,6 +31,7 @@ part 'strings_en.g.dart';
/// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check
enum AppLocale with BaseAppLocale<AppLocale, Translations> {
en(languageCode: 'en'),
ast(languageCode: 'ast'),
es(languageCode: 'es'),
pt(languageCode: 'pt');
@ -69,6 +71,12 @@ enum AppLocale with BaseAppLocale<AppLocale, Translations> {
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.ast:
return l_ast.TranslationsAst(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.es:
return l_es.TranslationsEs(
overrides: overrides,

File diff suppressed because it is too large Load diff

View file

@ -219,6 +219,9 @@ class Translations$settings$en {
/// en: 'Português'
String get langPt => 'Português';
/// en: 'Asturianu'
String get langAst => 'Asturianu';
/// en: 'About'
String get about => 'About';
@ -1101,6 +1104,12 @@ class Translations$market$en {
/// en: 'No seeds shared near you yet'
String get empty => 'No seeds shared near you yet';
/// en: 'Search these seeds'
String get searchHint => 'Search these seeds';
/// en: 'No shared seeds match your search'
String get noMatches => 'No shared seeds match your search';
/// en: 'Near you'
String get near => 'Near you';
@ -1778,6 +1787,7 @@ extension on Translations {
'settings.langEs' => 'Español',
'settings.langEn' => 'English',
'settings.langPt' => 'Português',
'settings.langAst' => 'Asturianu',
'settings.about' => 'About',
'settings.aboutText' => 'Local-first, encrypted inventory for traditional seeds. AGPL-3.0.',
'settings.aboutOpen' => 'About Tanemaki',
@ -2056,6 +2066,8 @@ extension on Translations {
'market.setAreaBody' => 'Tell the market roughly where you are to see seeds nearby.',
'market.searching' => 'Looking around your area…',
'market.empty' => 'No seeds shared near you yet',
'market.searchHint' => 'Search these seeds',
'market.noMatches' => 'No shared seeds match your search',
'market.near' => 'Near you',
'market.contact' => 'Message',
'market.mine' => 'You',

View file

@ -620,6 +620,8 @@ class _Translations$market$es extends Translations$market$en {
@override String get setAreaBody => 'Dile al mercado tu zona aproximada para ver semillas cerca.';
@override String get searching => 'Buscando por tu zona…';
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
@override String get searchHint => 'Buscar entre estas semillas';
@override String get noMatches => 'Ninguna semilla compartida coincide con tu búsqueda';
@override String get near => 'Cerca de ti';
@override String get contact => 'Mensaje';
@override String get mine => '';
@ -1358,6 +1360,8 @@ extension on TranslationsEs {
'market.setAreaBody' => 'Dile al mercado tu zona aproximada para ver semillas cerca.',
'market.searching' => 'Buscando por tu zona…',
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
'market.searchHint' => 'Buscar entre estas semillas',
'market.noMatches' => 'Ninguna semilla compartida coincide con tu búsqueda',
'market.near' => 'Cerca de ti',
'market.contact' => 'Mensaje',
'market.mine' => '',

View file

@ -157,6 +157,7 @@ class _Translations$settings$pt extends Translations$settings$en {
@override String get langEs => 'Español';
@override String get langEn => 'English';
@override String get langPt => 'Português';
@override String get langAst => 'Asturianu';
@override String get about => 'Acerca de';
@override String get aboutText => 'Inventário local e cifrado para sementes tradicionais. AGPL-3.0.';
@override String get aboutOpen => 'Acerca do Tanemaki';
@ -616,6 +617,8 @@ class _Translations$market$pt extends Translations$market$en {
@override String get setAreaBody => 'Diz ao mercado a tua zona aproximada para ver sementes por perto.';
@override String get searching => 'A procurar pela tua zona…';
@override String get empty => 'Ainda não há sementes partilhadas perto de ti';
@override String get searchHint => 'Procurar nestas sementes';
@override String get noMatches => 'Nenhuma semente partilhada corresponde à procura';
@override String get near => 'Perto de ti';
@override String get contact => 'Mensagem';
@override String get mine => 'Tu';
@ -1076,6 +1079,7 @@ extension on TranslationsPt {
'settings.langEs' => 'Español',
'settings.langEn' => 'English',
'settings.langPt' => 'Português',
'settings.langAst' => 'Asturianu',
'settings.about' => 'Acerca de',
'settings.aboutText' => 'Inventário local e cifrado para sementes tradicionais. AGPL-3.0.',
'settings.aboutOpen' => 'Acerca do Tanemaki',
@ -1350,6 +1354,8 @@ extension on TranslationsPt {
'market.setAreaBody' => 'Diz ao mercado a tua zona aproximada para ver sementes por perto.',
'market.searching' => 'A procurar pela tua zona…',
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
'market.searchHint' => 'Procurar nestas sementes',
'market.noMatches' => 'Nenhuma semente partilhada corresponde à procura',
'market.near' => 'Perto de ti',
'market.contact' => 'Mensagem',
'market.mine' => 'Tu',