fix(block2): sharing config UX + discovery + labels (raw feedback batch)
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.
This commit is contained in:
parent
d7136ec2c2
commit
5f87ad2d56
11 changed files with 208 additions and 90 deletions
|
|
@ -12,8 +12,8 @@
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"tagline": "Share and grow local seeds",
|
"tagline": "Share and grow local seeds",
|
||||||
"openMarket": "Open market",
|
"openMarket": "Market",
|
||||||
"openMarketSubtitle": "Browse and exchange",
|
"openMarketSubtitle": "Discover and share seeds nearby",
|
||||||
"yourInventory": "Your inventory",
|
"yourInventory": "Your inventory",
|
||||||
"yourInventorySubtitle": "Manage your seeds"
|
"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.",
|
"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",
|
"areaLabel": "Your area",
|
||||||
"areaHelp": "Kept rough on purpose — your zone, never an exact spot.",
|
"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",
|
"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",
|
"serversAdvanced": "Add another server",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"tagline": "Comparte y cultiva semillas locales",
|
"tagline": "Comparte y cultiva semillas locales",
|
||||||
"openMarket": "Mercado abierto",
|
"openMarket": "Mercado",
|
||||||
"openMarketSubtitle": "Explora e intercambia",
|
"openMarketSubtitle": "Descubre y comparte semillas cerca",
|
||||||
"yourInventory": "Tu inventario",
|
"yourInventory": "Tu inventario",
|
||||||
"yourInventorySubtitle": "Gestiona tus semillas"
|
"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.",
|
"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",
|
"areaLabel": "Tu zona",
|
||||||
"areaHelp": "Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.",
|
"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",
|
"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",
|
"serversAdvanced": "Añadir otro servidor",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"saved": "Guardado",
|
"saved": "Guardado",
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"tagline": "Partilha e cultiva sementes locais",
|
"tagline": "Partilha e cultiva sementes locais",
|
||||||
"openMarket": "Mercado aberto",
|
"openMarket": "Mercado",
|
||||||
"openMarketSubtitle": "Descobre e troca",
|
"openMarketSubtitle": "Descobre e partilha sementes por perto",
|
||||||
"yourInventory": "O teu inventário",
|
"yourInventory": "O teu inventário",
|
||||||
"yourInventorySubtitle": "Gere as tuas sementes"
|
"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.",
|
"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",
|
"areaLabel": "A tua zona",
|
||||||
"areaHelp": "Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.",
|
"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",
|
"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",
|
"serversAdvanced": "Adicionar outro servidor",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"saved": "Guardado",
|
"saved": "Guardado",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 3
|
/// 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
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint, unused_import
|
// ignore_for_file: type=lint, unused_import
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,11 @@ class Translations$home$en {
|
||||||
/// en: 'Share and grow local seeds'
|
/// en: 'Share and grow local seeds'
|
||||||
String get tagline => 'Share and grow local seeds';
|
String get tagline => 'Share and grow local seeds';
|
||||||
|
|
||||||
/// en: 'Open market'
|
/// en: 'Market'
|
||||||
String get openMarket => 'Open market';
|
String get openMarket => 'Market';
|
||||||
|
|
||||||
/// en: 'Browse and exchange'
|
/// en: 'Discover and share seeds nearby'
|
||||||
String get openMarketSubtitle => 'Browse and exchange';
|
String get openMarketSubtitle => 'Discover and share seeds nearby';
|
||||||
|
|
||||||
/// en: 'Your inventory'
|
/// en: 'Your inventory'
|
||||||
String get yourInventory => '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.'
|
/// en: 'Kept rough on purpose — your zone, never an exact spot.'
|
||||||
String get areaHelp => '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'
|
/// en: 'Community servers'
|
||||||
String get serversLabel => '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.'
|
/// en: 'One address per line (wss://…). Leave the defaults if unsure.'
|
||||||
String get serversHelp => 'Pick one or more shared servers. Anyone can use them — or add your own community\'s below.';
|
String get serversHelp => 'One address per line (wss://…). Leave the defaults if unsure.';
|
||||||
|
|
||||||
/// en: 'Add another server'
|
/// en: 'Add another server'
|
||||||
String get serversAdvanced => 'Add another server';
|
String get serversAdvanced => 'Add another server';
|
||||||
|
|
@ -1726,8 +1741,8 @@ extension on Translations {
|
||||||
'common.type' => 'Type',
|
'common.type' => 'Type',
|
||||||
'common.comingSoon' => 'Coming soon',
|
'common.comingSoon' => 'Coming soon',
|
||||||
'home.tagline' => 'Share and grow local seeds',
|
'home.tagline' => 'Share and grow local seeds',
|
||||||
'home.openMarket' => 'Open market',
|
'home.openMarket' => 'Market',
|
||||||
'home.openMarketSubtitle' => 'Browse and exchange',
|
'home.openMarketSubtitle' => 'Discover and share seeds nearby',
|
||||||
'home.yourInventory' => 'Your inventory',
|
'home.yourInventory' => 'Your inventory',
|
||||||
'home.yourInventorySubtitle' => 'Manage your seeds',
|
'home.yourInventorySubtitle' => 'Manage your seeds',
|
||||||
'photo.camera' => 'Take a photo',
|
'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.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.areaLabel' => 'Your area',
|
||||||
'market.areaHelp' => 'Kept rough on purpose — your zone, never an exact spot.',
|
'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.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.serversAdvanced' => 'Add another server',
|
||||||
'market.save' => 'Save',
|
'market.save' => 'Save',
|
||||||
'market.saved' => 'Saved',
|
'market.saved' => 'Saved',
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ class _Translations$home$es extends Translations$home$en {
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get tagline => 'Comparte y cultiva semillas locales';
|
@override String get tagline => 'Comparte y cultiva semillas locales';
|
||||||
@override String get openMarket => 'Mercado abierto';
|
@override String get openMarket => 'Mercado';
|
||||||
@override String get openMarketSubtitle => 'Explora e intercambia';
|
@override String get openMarketSubtitle => 'Descubre y comparte semillas cerca';
|
||||||
@override String get yourInventory => 'Tu inventario';
|
@override String get yourInventory => 'Tu inventario';
|
||||||
@override String get yourInventorySubtitle => 'Gestiona tus semillas';
|
@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 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 areaLabel => 'Tu zona';
|
||||||
@override String get areaHelp => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.';
|
@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 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 serversAdvanced => 'Añadir otro servidor';
|
||||||
@override String get save => 'Guardar';
|
@override String get save => 'Guardar';
|
||||||
@override String get saved => 'Guardado';
|
@override String get saved => 'Guardado';
|
||||||
|
|
@ -1048,8 +1053,8 @@ extension on TranslationsEs {
|
||||||
'common.type' => 'Tipo',
|
'common.type' => 'Tipo',
|
||||||
'common.comingSoon' => 'Pronto',
|
'common.comingSoon' => 'Pronto',
|
||||||
'home.tagline' => 'Comparte y cultiva semillas locales',
|
'home.tagline' => 'Comparte y cultiva semillas locales',
|
||||||
'home.openMarket' => 'Mercado abierto',
|
'home.openMarket' => 'Mercado',
|
||||||
'home.openMarketSubtitle' => 'Explora e intercambia',
|
'home.openMarketSubtitle' => 'Descubre y comparte semillas cerca',
|
||||||
'home.yourInventory' => 'Tu inventario',
|
'home.yourInventory' => 'Tu inventario',
|
||||||
'home.yourInventorySubtitle' => 'Gestiona tus semillas',
|
'home.yourInventorySubtitle' => 'Gestiona tus semillas',
|
||||||
'photo.camera' => 'Hacer una foto',
|
'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.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.areaLabel' => 'Tu zona',
|
||||||
'market.areaHelp' => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.',
|
'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.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.serversAdvanced' => 'Añadir otro servidor',
|
||||||
'market.save' => 'Guardar',
|
'market.save' => 'Guardar',
|
||||||
'market.saved' => 'Guardado',
|
'market.saved' => 'Guardado',
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ class _Translations$home$pt extends Translations$home$en {
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get tagline => 'Partilha e cultiva sementes locais';
|
@override String get tagline => 'Partilha e cultiva sementes locais';
|
||||||
@override String get openMarket => 'Mercado aberto';
|
@override String get openMarket => 'Mercado';
|
||||||
@override String get openMarketSubtitle => 'Descobre e troca';
|
@override String get openMarketSubtitle => 'Descobre e partilha sementes por perto';
|
||||||
@override String get yourInventory => 'O teu inventário';
|
@override String get yourInventory => 'O teu inventário';
|
||||||
@override String get yourInventorySubtitle => 'Gere as tuas sementes';
|
@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 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 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 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 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 serversAdvanced => 'Adicionar outro servidor';
|
||||||
@override String get save => 'Guardar';
|
@override String get save => 'Guardar';
|
||||||
@override String get saved => 'Guardado';
|
@override String get saved => 'Guardado';
|
||||||
|
|
@ -1044,8 +1049,8 @@ extension on TranslationsPt {
|
||||||
'common.type' => 'Tipo',
|
'common.type' => 'Tipo',
|
||||||
'common.comingSoon' => 'Em breve',
|
'common.comingSoon' => 'Em breve',
|
||||||
'home.tagline' => 'Partilha e cultiva sementes locais',
|
'home.tagline' => 'Partilha e cultiva sementes locais',
|
||||||
'home.openMarket' => 'Mercado aberto',
|
'home.openMarket' => 'Mercado',
|
||||||
'home.openMarketSubtitle' => 'Descobre e troca',
|
'home.openMarketSubtitle' => 'Descobre e partilha sementes por perto',
|
||||||
'home.yourInventory' => 'O teu inventário',
|
'home.yourInventory' => 'O teu inventário',
|
||||||
'home.yourInventorySubtitle' => 'Gere as tuas sementes',
|
'home.yourInventorySubtitle' => 'Gere as tuas sementes',
|
||||||
'photo.camera' => 'Tirar uma foto',
|
'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.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.areaLabel' => 'A tua zona',
|
||||||
'market.areaHelp' => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.',
|
'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.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.serversAdvanced' => 'Adicionar outro servidor',
|
||||||
'market.save' => 'Guardar',
|
'market.save' => 'Guardar',
|
||||||
'market.saved' => 'Guardado',
|
'market.saved' => 'Guardado',
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,13 @@ class GeolocatorCoarseLocation implements CoarseLocationProvider {
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
permission = await Geolocator.requestPermission();
|
permission = await Geolocator.requestPermission();
|
||||||
}
|
}
|
||||||
// Give the user a way out of a permanent denial.
|
// Denied (or turned off): return null quietly — the UI shows an actionable
|
||||||
if (permission == LocationPermission.deniedForever) {
|
// message. We don't yank the user into system settings.
|
||||||
await Geolocator.openAppSettings();
|
if (permission == LocationPermission.denied ||
|
||||||
return null;
|
permission == LocationPermission.deniedForever) {
|
||||||
}
|
|
||||||
if (permission == LocationPermission.denied) return null;
|
|
||||||
if (!await Geolocator.isLocationServiceEnabled()) {
|
|
||||||
await Geolocator.openLocationSettings();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (!await Geolocator.isLocationServiceEnabled()) return null;
|
||||||
|
|
||||||
// A fresh coarse fix can take a while (or never come indoors); fall back
|
// A fresh coarse fix can take a while (or never come indoors); fall back
|
||||||
// to the last known position so the button stays responsive.
|
// to the last known position so the button stays responsive.
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class OffersCubit extends Cubit<OffersState> {
|
||||||
/// (null in tests, where the transport is a fake with nothing to close).
|
/// (null in tests, where the transport is a fake with nothing to close).
|
||||||
final Future<void> Function()? _onDispose;
|
final Future<void> Function()? _onDispose;
|
||||||
StreamSubscription<Offer>? _sub;
|
StreamSubscription<Offer>? _sub;
|
||||||
|
Timer? _searchTimeout;
|
||||||
|
|
||||||
/// Whether a live transport is available (relay configured and reachable).
|
/// Whether a live transport is available (relay configured and reachable).
|
||||||
bool get isOnline => _transport != null;
|
bool get isOnline => _transport != null;
|
||||||
|
|
@ -88,6 +89,7 @@ class OffersCubit extends Cubit<OffersState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _sub?.cancel();
|
await _sub?.cancel();
|
||||||
|
_searchTimeout?.cancel();
|
||||||
emit(OffersState(
|
emit(OffersState(
|
||||||
areaGeohash: geohashPrefix,
|
areaGeohash: geohashPrefix,
|
||||||
searching: true,
|
searching: true,
|
||||||
|
|
@ -99,6 +101,14 @@ class OffersCubit extends Cubit<OffersState> {
|
||||||
onError: (Object e) =>
|
onError: (Object e) =>
|
||||||
emit(state.copyWith(searching: false, error: () => '$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
|
/// Publishes [offer]; returns the transport's verdict. No-op result when
|
||||||
|
|
@ -157,6 +167,7 @@ class OffersCubit extends Cubit<OffersState> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
|
_searchTimeout?.cancel();
|
||||||
await _sub?.cancel();
|
await _sub?.cancel();
|
||||||
await _onDispose?.call();
|
await _onDispose?.call();
|
||||||
return super.close();
|
return super.close();
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,7 @@ class _ConfigSheet extends StatefulWidget {
|
||||||
|
|
||||||
class _ConfigSheetState extends State<_ConfigSheet> {
|
class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
final _area = TextEditingController();
|
final _area = TextEditingController();
|
||||||
|
final _servers = TextEditingController();
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
bool _locationBusy = false;
|
bool _locationBusy = false;
|
||||||
String? _locationError;
|
String? _locationError;
|
||||||
|
|
@ -443,13 +444,13 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
|
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
_area.text = await widget.settings.areaGeohash();
|
_area.text = await widget.settings.areaGeohash();
|
||||||
|
_servers.text = (await widget.settings.relayUrls()).join('\n');
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _save() async {
|
Future<void> _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.setAreaGeohash(_area.text);
|
||||||
|
await widget.settings.setRelayUrls(_servers.text.split('\n'));
|
||||||
if (mounted) Navigator.of(context).pop(true);
|
if (mounted) Navigator.of(context).pop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,12 +478,14 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_area.dispose();
|
_area.dispose();
|
||||||
|
_servers.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final t = context.t;
|
final t = context.t;
|
||||||
|
final hasArea = _area.text.trim().isNotEmpty;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 20,
|
left: 20,
|
||||||
|
|
@ -491,7 +494,8 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
|
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
|
||||||
),
|
),
|
||||||
child: _loading
|
child: _loading
|
||||||
? const SizedBox(height: 120, child: Center(child: CircularProgressIndicator()))
|
? const SizedBox(
|
||||||
|
height: 120, child: Center(child: CircularProgressIndicator()))
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
@ -508,23 +512,16 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
t.market.setupIntro,
|
t.market.setupIntro,
|
||||||
style: const TextStyle(color: seedMuted, fontSize: 13, height: 1.4),
|
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: 18),
|
||||||
|
// Setting your area from device location is the human path;
|
||||||
|
// typing a code is the advanced fallback.
|
||||||
if (widget.location != null)
|
if (widget.location != null)
|
||||||
Align(
|
FilledButton.tonalIcon(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
child: TextButton.icon(
|
|
||||||
key: const Key('market.useLocation'),
|
key: const Key('market.useLocation'),
|
||||||
|
onPressed: _locationBusy ? null : _useLocation,
|
||||||
icon: _locationBusy
|
icon: _locationBusy
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
|
|
@ -533,15 +530,74 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
)
|
)
|
||||||
: const Icon(Icons.my_location, size: 18),
|
: const Icon(Icons.my_location, size: 18),
|
||||||
label: Text(t.market.useLocation),
|
label: Text(t.market.useLocation),
|
||||||
onPressed: _locationBusy ? null : _useLocation,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (_locationError != null)
|
if (_locationError != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 6),
|
||||||
child: Text(
|
child: Text(
|
||||||
_locationError!,
|
_locationError!,
|
||||||
style: const TextStyle(color: Color(0xFFB3261E), fontSize: 12),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,7 @@ void main() {
|
||||||
await tester.tap(find.byKey(const Key('market.useLocation')));
|
await tester.tap(find.byKey(const Key('market.useLocation')));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final area = tester.widget<TextField>(find.byKey(const Key('market.area')));
|
// The area status flips to "set" once a coarse location is captured.
|
||||||
expect(area.controller!.text, hasLength(5));
|
expect(find.textContaining('Your area is set'), findsOneWidget);
|
||||||
expect(area.controller!.text, matches(RegExp(r'^[0-9a-z]+$')));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue