Merge branch 'spike/block2-derisking'
This commit is contained in:
commit
552f1737bf
17 changed files with 428 additions and 108 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"
|
||||||
},
|
},
|
||||||
|
|
@ -339,6 +339,11 @@
|
||||||
"notSetUp": "Sharing isn't set up yet",
|
"notSetUp": "Sharing isn't set up yet",
|
||||||
"notSetUpBody": "Turn on sharing to see and give seeds to people near you. It stays rough — your zone, never your exact address.",
|
"notSetUpBody": "Turn on sharing to see and give seeds to people near you. It stays rough — your zone, never your exact address.",
|
||||||
"setUp": "Set up sharing",
|
"setUp": "Set up sharing",
|
||||||
|
"cantReach": "Can't reach the community servers right now",
|
||||||
|
"cantReachBody": "Try again, or check the servers under Advanced setup.",
|
||||||
|
"retry": "Retry",
|
||||||
|
"setArea": "Set your area",
|
||||||
|
"setAreaBody": "Tell the market roughly where you are to see seeds nearby.",
|
||||||
"searching": "Looking around your area…",
|
"searching": "Looking around your area…",
|
||||||
"empty": "No seeds shared near you yet",
|
"empty": "No seeds shared near you yet",
|
||||||
"near": "Near you",
|
"near": "Near you",
|
||||||
|
|
@ -348,8 +353,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",
|
||||||
|
|
@ -367,6 +377,8 @@
|
||||||
"nameHint": "How others see you",
|
"nameHint": "How others see you",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"aboutHint": "A short line — what you grow, where",
|
"aboutHint": "A short line — what you grow, where",
|
||||||
|
"g1": "Ğ1 address (optional)",
|
||||||
|
"g1Hint": "So people can pay you in Ğ1 — separate from your key",
|
||||||
"yourId": "Your identity",
|
"yourId": "Your identity",
|
||||||
"idHelp": "Share this so people can recognise you",
|
"idHelp": "Share this so people can recognise you",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
|
|
@ -383,7 +395,9 @@
|
||||||
"hint": "Write a message…",
|
"hint": "Write a message…",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"empty": "No messages yet — say hello",
|
"empty": "No messages yet — say hello",
|
||||||
"offline": "Set up sharing to send messages"
|
"offline": "Set up sharing to send messages",
|
||||||
|
"payG1": "Pay in Ğ1",
|
||||||
|
"g1Copied": "Ğ1 address copied — paste it in your wallet"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"none": "No one vouches for them yet",
|
"none": "No one vouches for them yet",
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
@ -339,6 +339,11 @@
|
||||||
"notSetUp": "Aún no has configurado el compartir",
|
"notSetUp": "Aún no has configurado el compartir",
|
||||||
"notSetUpBody": "Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.",
|
"notSetUpBody": "Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.",
|
||||||
"setUp": "Configurar el compartir",
|
"setUp": "Configurar el compartir",
|
||||||
|
"cantReach": "No se puede conectar con los servidores ahora mismo",
|
||||||
|
"cantReachBody": "Inténtalo de nuevo, o revisa los servidores en la configuración avanzada.",
|
||||||
|
"retry": "Reintentar",
|
||||||
|
"setArea": "Indica tu zona",
|
||||||
|
"setAreaBody": "Dile al mercado tu zona aproximada para ver semillas cerca.",
|
||||||
"searching": "Buscando por tu zona…",
|
"searching": "Buscando por tu zona…",
|
||||||
"empty": "Aún no hay semillas compartidas cerca de ti",
|
"empty": "Aún no hay semillas compartidas cerca de ti",
|
||||||
"near": "Cerca de ti",
|
"near": "Cerca de ti",
|
||||||
|
|
@ -348,8 +353,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",
|
||||||
|
|
@ -367,6 +377,8 @@
|
||||||
"nameHint": "Cómo te ven los demás",
|
"nameHint": "Cómo te ven los demás",
|
||||||
"about": "Sobre ti",
|
"about": "Sobre ti",
|
||||||
"aboutHint": "Una línea — qué cultivas, dónde",
|
"aboutHint": "Una línea — qué cultivas, dónde",
|
||||||
|
"g1": "Dirección Ğ1 (opcional)",
|
||||||
|
"g1Hint": "Para que te paguen en Ğ1 — aparte de tu clave",
|
||||||
"yourId": "Tu identidad",
|
"yourId": "Tu identidad",
|
||||||
"idHelp": "Compártela para que te reconozcan",
|
"idHelp": "Compártela para que te reconozcan",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
|
|
@ -383,7 +395,9 @@
|
||||||
"hint": "Escribe un mensaje…",
|
"hint": "Escribe un mensaje…",
|
||||||
"send": "Enviar",
|
"send": "Enviar",
|
||||||
"empty": "Aún no hay mensajes — saluda",
|
"empty": "Aún no hay mensajes — saluda",
|
||||||
"offline": "Configura el compartir para enviar mensajes"
|
"offline": "Configura el compartir para enviar mensajes",
|
||||||
|
"payG1": "Pagar en Ğ1",
|
||||||
|
"g1Copied": "Dirección Ğ1 copiada — pégala en tu cartera"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"none": "Nadie los avala aún",
|
"none": "Nadie los avala aún",
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
@ -335,6 +335,11 @@
|
||||||
"notSetUp": "Ainda não configuraste a partilha",
|
"notSetUp": "Ainda não configuraste a partilha",
|
||||||
"notSetUpBody": "Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.",
|
"notSetUpBody": "Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.",
|
||||||
"setUp": "Configurar a partilha",
|
"setUp": "Configurar a partilha",
|
||||||
|
"cantReach": "Não é possível ligar aos servidores neste momento",
|
||||||
|
"cantReachBody": "Tenta de novo, ou verifica os servidores na configuração avançada.",
|
||||||
|
"retry": "Tentar de novo",
|
||||||
|
"setArea": "Indica a tua zona",
|
||||||
|
"setAreaBody": "Diz ao mercado a tua zona aproximada para ver sementes por perto.",
|
||||||
"searching": "A procurar pela tua zona…",
|
"searching": "A procurar pela tua zona…",
|
||||||
"empty": "Ainda não há sementes partilhadas perto de ti",
|
"empty": "Ainda não há sementes partilhadas perto de ti",
|
||||||
"near": "Perto de ti",
|
"near": "Perto de ti",
|
||||||
|
|
@ -344,8 +349,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",
|
||||||
|
|
@ -363,6 +373,8 @@
|
||||||
"nameHint": "Como os outros te veem",
|
"nameHint": "Como os outros te veem",
|
||||||
"about": "Sobre ti",
|
"about": "Sobre ti",
|
||||||
"aboutHint": "Uma linha — o que cultivas, onde",
|
"aboutHint": "Uma linha — o que cultivas, onde",
|
||||||
|
"g1": "Endereço Ğ1 (opcional)",
|
||||||
|
"g1Hint": "Para que te paguem em Ğ1 — separado da tua chave",
|
||||||
"yourId": "A tua identidade",
|
"yourId": "A tua identidade",
|
||||||
"idHelp": "Partilha-a para que te reconheçam",
|
"idHelp": "Partilha-a para que te reconheçam",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
|
|
@ -379,7 +391,9 @@
|
||||||
"hint": "Escreve uma mensagem…",
|
"hint": "Escreve uma mensagem…",
|
||||||
"send": "Enviar",
|
"send": "Enviar",
|
||||||
"empty": "Ainda não há mensagens — diz olá",
|
"empty": "Ainda não há mensagens — diz olá",
|
||||||
"offline": "Configura a partilha para enviar mensagens"
|
"offline": "Configura a partilha para enviar mensagens",
|
||||||
|
"payG1": "Pagar em Ğ1",
|
||||||
|
"g1Copied": "Endereço Ğ1 copiado — cola-o na tua carteira"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"none": "Ainda ninguém os avaliza",
|
"none": "Ainda ninguém os avaliza",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 3
|
/// Locales: 3
|
||||||
/// Strings: 1031 (343 per locale)
|
/// Strings: 1073 (357 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2026-07-10 at 10:36 UTC
|
/// Built on 2026-07-10 at 11:35 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';
|
||||||
|
|
@ -1080,6 +1080,21 @@ class Translations$market$en {
|
||||||
/// en: 'Set up sharing'
|
/// en: 'Set up sharing'
|
||||||
String get setUp => 'Set up sharing';
|
String get setUp => 'Set up sharing';
|
||||||
|
|
||||||
|
/// en: 'Can't reach the community servers right now'
|
||||||
|
String get cantReach => 'Can\'t reach the community servers right now';
|
||||||
|
|
||||||
|
/// en: 'Try again, or check the servers under Advanced setup.'
|
||||||
|
String get cantReachBody => 'Try again, or check the servers under Advanced setup.';
|
||||||
|
|
||||||
|
/// en: 'Retry'
|
||||||
|
String get retry => 'Retry';
|
||||||
|
|
||||||
|
/// en: 'Set your area'
|
||||||
|
String get setArea => 'Set your area';
|
||||||
|
|
||||||
|
/// en: 'Tell the market roughly where you are to see seeds nearby.'
|
||||||
|
String get setAreaBody => 'Tell the market roughly where you are to see seeds nearby.';
|
||||||
|
|
||||||
/// en: 'Looking around your area…'
|
/// en: 'Looking around your area…'
|
||||||
String get searching => 'Looking around your area…';
|
String get searching => 'Looking around your area…';
|
||||||
|
|
||||||
|
|
@ -1107,11 +1122,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';
|
||||||
|
|
@ -1167,6 +1197,12 @@ class Translations$profile$en {
|
||||||
/// en: 'A short line — what you grow, where'
|
/// en: 'A short line — what you grow, where'
|
||||||
String get aboutHint => 'A short line — what you grow, where';
|
String get aboutHint => 'A short line — what you grow, where';
|
||||||
|
|
||||||
|
/// en: 'Ğ1 address (optional)'
|
||||||
|
String get g1 => 'Ğ1 address (optional)';
|
||||||
|
|
||||||
|
/// en: 'So people can pay you in Ğ1 — separate from your key'
|
||||||
|
String get g1Hint => 'So people can pay you in Ğ1 — separate from your key';
|
||||||
|
|
||||||
/// en: 'Your identity'
|
/// en: 'Your identity'
|
||||||
String get yourId => 'Your identity';
|
String get yourId => 'Your identity';
|
||||||
|
|
||||||
|
|
@ -1223,6 +1259,12 @@ class Translations$chat$en {
|
||||||
|
|
||||||
/// en: 'Set up sharing to send messages'
|
/// en: 'Set up sharing to send messages'
|
||||||
String get offline => 'Set up sharing to send messages';
|
String get offline => 'Set up sharing to send messages';
|
||||||
|
|
||||||
|
/// en: 'Pay in Ğ1'
|
||||||
|
String get payG1 => 'Pay in Ğ1';
|
||||||
|
|
||||||
|
/// en: 'Ğ1 address copied — paste it in your wallet'
|
||||||
|
String get g1Copied => 'Ğ1 address copied — paste it in your wallet';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: trust
|
// Path: trust
|
||||||
|
|
@ -1714,8 +1756,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',
|
||||||
|
|
@ -2007,6 +2049,11 @@ extension on Translations {
|
||||||
'market.notSetUp' => 'Sharing isn\'t set up yet',
|
'market.notSetUp' => 'Sharing isn\'t set up yet',
|
||||||
'market.notSetUpBody' => 'Turn on sharing to see and give seeds to people near you. It stays rough — your zone, never your exact address.',
|
'market.notSetUpBody' => 'Turn on sharing to see and give seeds to people near you. It stays rough — your zone, never your exact address.',
|
||||||
'market.setUp' => 'Set up sharing',
|
'market.setUp' => 'Set up sharing',
|
||||||
|
'market.cantReach' => 'Can\'t reach the community servers right now',
|
||||||
|
'market.cantReachBody' => 'Try again, or check the servers under Advanced setup.',
|
||||||
|
'market.retry' => 'Retry',
|
||||||
|
'market.setArea' => 'Set your area',
|
||||||
|
'market.setAreaBody' => 'Tell the market roughly where you are to see seeds nearby.',
|
||||||
'market.searching' => 'Looking around your area…',
|
'market.searching' => 'Looking around your area…',
|
||||||
'market.empty' => 'No seeds shared near you yet',
|
'market.empty' => 'No seeds shared near you yet',
|
||||||
'market.near' => 'Near you',
|
'market.near' => 'Near you',
|
||||||
|
|
@ -2016,8 +2063,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',
|
||||||
|
|
@ -2033,6 +2085,8 @@ extension on Translations {
|
||||||
'profile.nameHint' => 'How others see you',
|
'profile.nameHint' => 'How others see you',
|
||||||
'profile.about' => 'About',
|
'profile.about' => 'About',
|
||||||
'profile.aboutHint' => 'A short line — what you grow, where',
|
'profile.aboutHint' => 'A short line — what you grow, where',
|
||||||
|
'profile.g1' => 'Ğ1 address (optional)',
|
||||||
|
'profile.g1Hint' => 'So people can pay you in Ğ1 — separate from your key',
|
||||||
'profile.yourId' => 'Your identity',
|
'profile.yourId' => 'Your identity',
|
||||||
'profile.idHelp' => 'Share this so people can recognise you',
|
'profile.idHelp' => 'Share this so people can recognise you',
|
||||||
'profile.copy' => 'Copy',
|
'profile.copy' => 'Copy',
|
||||||
|
|
@ -2046,6 +2100,8 @@ extension on Translations {
|
||||||
'chat.send' => 'Send',
|
'chat.send' => 'Send',
|
||||||
'chat.empty' => 'No messages yet — say hello',
|
'chat.empty' => 'No messages yet — say hello',
|
||||||
'chat.offline' => 'Set up sharing to send messages',
|
'chat.offline' => 'Set up sharing to send messages',
|
||||||
|
'chat.payG1' => 'Pay in Ğ1',
|
||||||
|
'chat.g1Copied' => 'Ğ1 address copied — paste it in your wallet',
|
||||||
'trust.none' => 'No one vouches for them yet',
|
'trust.none' => 'No one vouches for them yet',
|
||||||
'trust.count' => ({required Object n}) => 'Vouched for by ${n}',
|
'trust.count' => ({required Object n}) => 'Vouched for by ${n}',
|
||||||
'trust.vouch' => 'I know this person',
|
'trust.vouch' => 'I know this person',
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
@ -613,6 +613,11 @@ class _Translations$market$es extends Translations$market$en {
|
||||||
@override String get notSetUp => 'Aún no has configurado el compartir';
|
@override String get notSetUp => 'Aún no has configurado el compartir';
|
||||||
@override String get notSetUpBody => 'Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.';
|
@override String get notSetUpBody => 'Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.';
|
||||||
@override String get setUp => 'Configurar el compartir';
|
@override String get setUp => 'Configurar el compartir';
|
||||||
|
@override String get cantReach => 'No se puede conectar con los servidores ahora mismo';
|
||||||
|
@override String get cantReachBody => 'Inténtalo de nuevo, o revisa los servidores en la configuración avanzada.';
|
||||||
|
@override String get retry => 'Reintentar';
|
||||||
|
@override String get setArea => 'Indica tu zona';
|
||||||
|
@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 searching => 'Buscando por tu zona…';
|
||||||
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
|
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
|
||||||
@override String get near => 'Cerca de ti';
|
@override String get near => 'Cerca de ti';
|
||||||
|
|
@ -622,8 +627,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';
|
||||||
|
|
@ -648,6 +658,8 @@ class _Translations$profile$es extends Translations$profile$en {
|
||||||
@override String get nameHint => 'Cómo te ven los demás';
|
@override String get nameHint => 'Cómo te ven los demás';
|
||||||
@override String get about => 'Sobre ti';
|
@override String get about => 'Sobre ti';
|
||||||
@override String get aboutHint => 'Una línea — qué cultivas, dónde';
|
@override String get aboutHint => 'Una línea — qué cultivas, dónde';
|
||||||
|
@override String get g1 => 'Dirección Ğ1 (opcional)';
|
||||||
|
@override String get g1Hint => 'Para que te paguen en Ğ1 — aparte de tu clave';
|
||||||
@override String get yourId => 'Tu identidad';
|
@override String get yourId => 'Tu identidad';
|
||||||
@override String get idHelp => 'Compártela para que te reconozcan';
|
@override String get idHelp => 'Compártela para que te reconozcan';
|
||||||
@override String get copy => 'Copiar';
|
@override String get copy => 'Copiar';
|
||||||
|
|
@ -679,6 +691,8 @@ class _Translations$chat$es extends Translations$chat$en {
|
||||||
@override String get send => 'Enviar';
|
@override String get send => 'Enviar';
|
||||||
@override String get empty => 'Aún no hay mensajes — saluda';
|
@override String get empty => 'Aún no hay mensajes — saluda';
|
||||||
@override String get offline => 'Configura el compartir para enviar mensajes';
|
@override String get offline => 'Configura el compartir para enviar mensajes';
|
||||||
|
@override String get payG1 => 'Pagar en Ğ1';
|
||||||
|
@override String get g1Copied => 'Dirección Ğ1 copiada — pégala en tu cartera';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: trust
|
// Path: trust
|
||||||
|
|
@ -1044,8 +1058,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',
|
||||||
|
|
@ -1337,6 +1351,11 @@ extension on TranslationsEs {
|
||||||
'market.notSetUp' => 'Aún no has configurado el compartir',
|
'market.notSetUp' => 'Aún no has configurado el compartir',
|
||||||
'market.notSetUpBody' => 'Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.',
|
'market.notSetUpBody' => 'Activa el compartir para ver y dar semillas a gente cercana. Se mantiene aproximado — tu zona, nunca tu dirección exacta.',
|
||||||
'market.setUp' => 'Configurar el compartir',
|
'market.setUp' => 'Configurar el compartir',
|
||||||
|
'market.cantReach' => 'No se puede conectar con los servidores ahora mismo',
|
||||||
|
'market.cantReachBody' => 'Inténtalo de nuevo, o revisa los servidores en la configuración avanzada.',
|
||||||
|
'market.retry' => 'Reintentar',
|
||||||
|
'market.setArea' => 'Indica tu zona',
|
||||||
|
'market.setAreaBody' => 'Dile al mercado tu zona aproximada para ver semillas cerca.',
|
||||||
'market.searching' => 'Buscando por tu zona…',
|
'market.searching' => 'Buscando por tu zona…',
|
||||||
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
|
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
|
||||||
'market.near' => 'Cerca de ti',
|
'market.near' => 'Cerca de ti',
|
||||||
|
|
@ -1346,8 +1365,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',
|
||||||
|
|
@ -1363,6 +1387,8 @@ extension on TranslationsEs {
|
||||||
'profile.nameHint' => 'Cómo te ven los demás',
|
'profile.nameHint' => 'Cómo te ven los demás',
|
||||||
'profile.about' => 'Sobre ti',
|
'profile.about' => 'Sobre ti',
|
||||||
'profile.aboutHint' => 'Una línea — qué cultivas, dónde',
|
'profile.aboutHint' => 'Una línea — qué cultivas, dónde',
|
||||||
|
'profile.g1' => 'Dirección Ğ1 (opcional)',
|
||||||
|
'profile.g1Hint' => 'Para que te paguen en Ğ1 — aparte de tu clave',
|
||||||
'profile.yourId' => 'Tu identidad',
|
'profile.yourId' => 'Tu identidad',
|
||||||
'profile.idHelp' => 'Compártela para que te reconozcan',
|
'profile.idHelp' => 'Compártela para que te reconozcan',
|
||||||
'profile.copy' => 'Copiar',
|
'profile.copy' => 'Copiar',
|
||||||
|
|
@ -1376,6 +1402,8 @@ extension on TranslationsEs {
|
||||||
'chat.send' => 'Enviar',
|
'chat.send' => 'Enviar',
|
||||||
'chat.empty' => 'Aún no hay mensajes — saluda',
|
'chat.empty' => 'Aún no hay mensajes — saluda',
|
||||||
'chat.offline' => 'Configura el compartir para enviar mensajes',
|
'chat.offline' => 'Configura el compartir para enviar mensajes',
|
||||||
|
'chat.payG1' => 'Pagar en Ğ1',
|
||||||
|
'chat.g1Copied' => 'Dirección Ğ1 copiada — pégala en tu cartera',
|
||||||
'trust.none' => 'Nadie los avala aún',
|
'trust.none' => 'Nadie los avala aún',
|
||||||
'trust.count' => ({required Object n}) => 'Avalada por ${n}',
|
'trust.count' => ({required Object n}) => 'Avalada por ${n}',
|
||||||
'trust.vouch' => 'Conozco a esta persona',
|
'trust.vouch' => 'Conozco a esta persona',
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
@ -609,6 +609,11 @@ class _Translations$market$pt extends Translations$market$en {
|
||||||
@override String get notSetUp => 'Ainda não configuraste a partilha';
|
@override String get notSetUp => 'Ainda não configuraste a partilha';
|
||||||
@override String get notSetUpBody => 'Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.';
|
@override String get notSetUpBody => 'Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.';
|
||||||
@override String get setUp => 'Configurar a partilha';
|
@override String get setUp => 'Configurar a partilha';
|
||||||
|
@override String get cantReach => 'Não é possível ligar aos servidores neste momento';
|
||||||
|
@override String get cantReachBody => 'Tenta de novo, ou verifica os servidores na configuração avançada.';
|
||||||
|
@override String get retry => 'Tentar de novo';
|
||||||
|
@override String get setArea => 'Indica a tua zona';
|
||||||
|
@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 searching => 'A procurar pela tua zona…';
|
||||||
@override String get empty => 'Ainda não há sementes partilhadas perto de ti';
|
@override String get empty => 'Ainda não há sementes partilhadas perto de ti';
|
||||||
@override String get near => 'Perto de ti';
|
@override String get near => 'Perto de ti';
|
||||||
|
|
@ -618,8 +623,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';
|
||||||
|
|
@ -644,6 +654,8 @@ class _Translations$profile$pt extends Translations$profile$en {
|
||||||
@override String get nameHint => 'Como os outros te veem';
|
@override String get nameHint => 'Como os outros te veem';
|
||||||
@override String get about => 'Sobre ti';
|
@override String get about => 'Sobre ti';
|
||||||
@override String get aboutHint => 'Uma linha — o que cultivas, onde';
|
@override String get aboutHint => 'Uma linha — o que cultivas, onde';
|
||||||
|
@override String get g1 => 'Endereço Ğ1 (opcional)';
|
||||||
|
@override String get g1Hint => 'Para que te paguem em Ğ1 — separado da tua chave';
|
||||||
@override String get yourId => 'A tua identidade';
|
@override String get yourId => 'A tua identidade';
|
||||||
@override String get idHelp => 'Partilha-a para que te reconheçam';
|
@override String get idHelp => 'Partilha-a para que te reconheçam';
|
||||||
@override String get copy => 'Copiar';
|
@override String get copy => 'Copiar';
|
||||||
|
|
@ -675,6 +687,8 @@ class _Translations$chat$pt extends Translations$chat$en {
|
||||||
@override String get send => 'Enviar';
|
@override String get send => 'Enviar';
|
||||||
@override String get empty => 'Ainda não há mensagens — diz olá';
|
@override String get empty => 'Ainda não há mensagens — diz olá';
|
||||||
@override String get offline => 'Configura a partilha para enviar mensagens';
|
@override String get offline => 'Configura a partilha para enviar mensagens';
|
||||||
|
@override String get payG1 => 'Pagar em Ğ1';
|
||||||
|
@override String get g1Copied => 'Endereço Ğ1 copiado — cola-o na tua carteira';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: trust
|
// Path: trust
|
||||||
|
|
@ -1040,8 +1054,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',
|
||||||
|
|
@ -1329,6 +1343,11 @@ extension on TranslationsPt {
|
||||||
'market.notSetUp' => 'Ainda não configuraste a partilha',
|
'market.notSetUp' => 'Ainda não configuraste a partilha',
|
||||||
'market.notSetUpBody' => 'Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.',
|
'market.notSetUpBody' => 'Ativa a partilha para ver e dar sementes a pessoas por perto. Mantém-se aproximado — a tua zona, nunca a tua morada exata.',
|
||||||
'market.setUp' => 'Configurar a partilha',
|
'market.setUp' => 'Configurar a partilha',
|
||||||
|
'market.cantReach' => 'Não é possível ligar aos servidores neste momento',
|
||||||
|
'market.cantReachBody' => 'Tenta de novo, ou verifica os servidores na configuração avançada.',
|
||||||
|
'market.retry' => 'Tentar de novo',
|
||||||
|
'market.setArea' => 'Indica a tua zona',
|
||||||
|
'market.setAreaBody' => 'Diz ao mercado a tua zona aproximada para ver sementes por perto.',
|
||||||
'market.searching' => 'A procurar pela tua zona…',
|
'market.searching' => 'A procurar pela tua zona…',
|
||||||
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
|
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
|
||||||
'market.near' => 'Perto de ti',
|
'market.near' => 'Perto de ti',
|
||||||
|
|
@ -1338,8 +1357,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',
|
||||||
|
|
@ -1355,6 +1379,8 @@ extension on TranslationsPt {
|
||||||
'profile.nameHint' => 'Como os outros te veem',
|
'profile.nameHint' => 'Como os outros te veem',
|
||||||
'profile.about' => 'Sobre ti',
|
'profile.about' => 'Sobre ti',
|
||||||
'profile.aboutHint' => 'Uma linha — o que cultivas, onde',
|
'profile.aboutHint' => 'Uma linha — o que cultivas, onde',
|
||||||
|
'profile.g1' => 'Endereço Ğ1 (opcional)',
|
||||||
|
'profile.g1Hint' => 'Para que te paguem em Ğ1 — separado da tua chave',
|
||||||
'profile.yourId' => 'A tua identidade',
|
'profile.yourId' => 'A tua identidade',
|
||||||
'profile.idHelp' => 'Partilha-a para que te reconheçam',
|
'profile.idHelp' => 'Partilha-a para que te reconheçam',
|
||||||
'profile.copy' => 'Copiar',
|
'profile.copy' => 'Copiar',
|
||||||
|
|
@ -1368,6 +1394,8 @@ extension on TranslationsPt {
|
||||||
'chat.send' => 'Enviar',
|
'chat.send' => 'Enviar',
|
||||||
'chat.empty' => 'Ainda não há mensagens — diz olá',
|
'chat.empty' => 'Ainda não há mensagens — diz olá',
|
||||||
'chat.offline' => 'Configura a partilha para enviar mensagens',
|
'chat.offline' => 'Configura a partilha para enviar mensagens',
|
||||||
|
'chat.payG1' => 'Pagar em Ğ1',
|
||||||
|
'chat.g1Copied' => 'Endereço Ğ1 copiado — cola-o na tua carteira',
|
||||||
'trust.none' => 'Ainda ninguém os avaliza',
|
'trust.none' => 'Ainda ninguém os avaliza',
|
||||||
'trust.count' => ({required Object n}) => 'Avalizada por ${n}',
|
'trust.count' => ({required Object n}) => 'Avalizada por ${n}',
|
||||||
'trust.vouch' => 'Conheço esta pessoa',
|
'trust.vouch' => 'Conheço esta pessoa',
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,21 @@ class ProfileStore {
|
||||||
final SecretStore _store;
|
final SecretStore _store;
|
||||||
static const _nameKey = 'tane.social.profile.name';
|
static const _nameKey = 'tane.social.profile.name';
|
||||||
static const _aboutKey = 'tane.social.profile.about';
|
static const _aboutKey = 'tane.social.profile.about';
|
||||||
|
static const _g1Key = 'tane.social.profile.g1';
|
||||||
|
|
||||||
Future<String> name() async => (await _store.read(_nameKey)) ?? '';
|
Future<String> name() async => (await _store.read(_nameKey)) ?? '';
|
||||||
Future<String> about() async => (await _store.read(_aboutKey)) ?? '';
|
Future<String> about() async => (await _store.read(_aboutKey)) ?? '';
|
||||||
|
|
||||||
Future<void> save({required String name, required String about}) async {
|
/// Your Ğ1 (Duniter) address, if you chose to share one.
|
||||||
|
Future<String> g1() async => (await _store.read(_g1Key)) ?? '';
|
||||||
|
|
||||||
|
Future<void> save({
|
||||||
|
required String name,
|
||||||
|
required String about,
|
||||||
|
String g1 = '',
|
||||||
|
}) async {
|
||||||
await _store.write(_nameKey, name.trim());
|
await _store.write(_nameKey, name.trim());
|
||||||
await _store.write(_aboutKey, about.trim());
|
await _store.write(_aboutKey, about.trim());
|
||||||
|
await _store.write(_g1Key, g1.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../i18n/strings.g.dart';
|
import '../i18n/strings.g.dart';
|
||||||
import '../services/message_store.dart';
|
import '../services/message_store.dart';
|
||||||
|
|
@ -45,6 +47,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
TrustCubit? _trust;
|
TrustCubit? _trust;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
String? _peerName;
|
String? _peerName;
|
||||||
|
String? _peerG1;
|
||||||
final _input = TextEditingController();
|
final _input = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -94,9 +97,15 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
unawaited(() async {
|
unawaited(() async {
|
||||||
try {
|
try {
|
||||||
final profile = await live.profile.fetch(widget.peerPubkey);
|
final profile = await live.profile.fetch(widget.peerPubkey);
|
||||||
if (profile != null && profile.name.isNotEmpty && mounted) {
|
if (profile != null && mounted) {
|
||||||
await widget.profileCache!.setName(widget.peerPubkey, profile.name);
|
if (profile.name.isNotEmpty) {
|
||||||
setState(() => _peerName = profile.name);
|
await widget.profileCache!
|
||||||
|
.setName(widget.peerPubkey, profile.name);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
if (profile.name.isNotEmpty) _peerName = profile.name;
|
||||||
|
if (profile.g1.isNotEmpty) _peerG1 = profile.g1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// best effort
|
// best effort
|
||||||
|
|
@ -105,6 +114,30 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the peer's Ğ1 wallet so you can pay them (Ğ1 is separate from the
|
||||||
|
/// Nostr key — this uses the address they published in their profile). Falls
|
||||||
|
/// back to copying the address if no wallet handles the link.
|
||||||
|
///
|
||||||
|
/// NOTE: confirm the deep-link scheme for the target wallet (Ğ1nkgo / Ğecko /
|
||||||
|
/// Cesium) — this uses the Cesium web wallet as a universal fallback.
|
||||||
|
Future<void> _payG1() async {
|
||||||
|
final g1 = _peerG1;
|
||||||
|
if (g1 == null) return;
|
||||||
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
|
final t = context.t;
|
||||||
|
final uri = Uri.parse('https://demo.cesium.app/#/app/wallet/$g1/');
|
||||||
|
var launched = false;
|
||||||
|
try {
|
||||||
|
launched = await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
} catch (_) {
|
||||||
|
launched = false;
|
||||||
|
}
|
||||||
|
if (!launched && mounted) {
|
||||||
|
await Clipboard.setData(ClipboardData(text: g1));
|
||||||
|
messenger.showSnackBar(SnackBar(content: Text(t.chat.g1Copied)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _send() async {
|
Future<void> _send() async {
|
||||||
final cubit = _messages;
|
final cubit = _messages;
|
||||||
if (cubit == null || _input.text.trim().isEmpty) return;
|
if (cubit == null || _input.text.trim().isEmpty) return;
|
||||||
|
|
@ -126,9 +159,19 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final messages = _messages;
|
final messages = _messages;
|
||||||
final trust = _trust;
|
final trust = _trust;
|
||||||
|
final t = context.t;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(_peerName ?? shortPubkey(widget.peerPubkey)),
|
title: Text(_peerName ?? shortPubkey(widget.peerPubkey)),
|
||||||
|
actions: [
|
||||||
|
if (_peerG1 != null)
|
||||||
|
IconButton(
|
||||||
|
key: const Key('chat.payG1'),
|
||||||
|
icon: const Icon(Icons.payments_outlined),
|
||||||
|
tooltip: t.chat.payG1,
|
||||||
|
onPressed: _payG1,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: _loading || messages == null || trust == null
|
body: _loading || messages == null || trust == null
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ class MarketScreen extends StatefulWidget {
|
||||||
class _MarketScreenState extends State<MarketScreen> {
|
class _MarketScreenState extends State<MarketScreen> {
|
||||||
OffersCubit? _cubit;
|
OffersCubit? _cubit;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
|
bool _hasArea = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -64,6 +65,7 @@ class _MarketScreenState extends State<MarketScreen> {
|
||||||
final previous = _cubit;
|
final previous = _cubit;
|
||||||
setState(() {
|
setState(() {
|
||||||
_cubit = cubit;
|
_cubit = cubit;
|
||||||
|
_hasArea = area.isNotEmpty;
|
||||||
_loading = false;
|
_loading = false;
|
||||||
});
|
});
|
||||||
await previous?.close();
|
await previous?.close();
|
||||||
|
|
@ -171,6 +173,8 @@ class _MarketScreenState extends State<MarketScreen> {
|
||||||
value: cubit,
|
value: cubit,
|
||||||
child: MarketBody(
|
child: MarketBody(
|
||||||
onConfigure: _openConfig,
|
onConfigure: _openConfig,
|
||||||
|
onRetry: _init,
|
||||||
|
hasArea: _hasArea,
|
||||||
selfPubkey: widget.social.publicKeyHex,
|
selfPubkey: widget.social.publicKeyHex,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -181,12 +185,20 @@ class _MarketScreenState extends State<MarketScreen> {
|
||||||
class MarketBody extends StatelessWidget {
|
class MarketBody extends StatelessWidget {
|
||||||
const MarketBody({
|
const MarketBody({
|
||||||
required this.onConfigure,
|
required this.onConfigure,
|
||||||
|
this.onRetry,
|
||||||
|
this.hasArea = true,
|
||||||
this.selfPubkey,
|
this.selfPubkey,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final VoidCallback onConfigure;
|
final VoidCallback onConfigure;
|
||||||
|
|
||||||
|
/// Re-opens the connection (for the "can't reach servers" retry).
|
||||||
|
final VoidCallback? onRetry;
|
||||||
|
|
||||||
|
/// Whether the user has set their coarse area yet.
|
||||||
|
final bool hasArea;
|
||||||
|
|
||||||
/// This user's own pubkey, so we don't offer to message our own listings.
|
/// This user's own pubkey, so we don't offer to message our own listings.
|
||||||
final String? selfPubkey;
|
final String? selfPubkey;
|
||||||
|
|
||||||
|
|
@ -196,10 +208,21 @@ class MarketBody extends StatelessWidget {
|
||||||
return BlocBuilder<OffersCubit, OffersState>(
|
return BlocBuilder<OffersCubit, OffersState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (!context.read<OffersCubit>().isOnline) {
|
if (!context.read<OffersCubit>().isOnline) {
|
||||||
|
// Default community servers exist, so "offline" means unreachable —
|
||||||
|
// offer a retry rather than "set up sharing".
|
||||||
return _EmptyState(
|
return _EmptyState(
|
||||||
icon: Icons.wifi_tethering_off_outlined,
|
icon: Icons.wifi_tethering_off_outlined,
|
||||||
title: t.market.notSetUp,
|
title: t.market.cantReach,
|
||||||
body: t.market.notSetUpBody,
|
body: t.market.cantReachBody,
|
||||||
|
actionLabel: onRetry != null ? t.market.retry : null,
|
||||||
|
onAction: onRetry,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!hasArea) {
|
||||||
|
return _EmptyState(
|
||||||
|
icon: Icons.place_outlined,
|
||||||
|
title: t.market.setArea,
|
||||||
|
body: t.market.setAreaBody,
|
||||||
actionLabel: t.market.setUp,
|
actionLabel: t.market.setUp,
|
||||||
onAction: onConfigure,
|
onAction: onConfigure,
|
||||||
);
|
);
|
||||||
|
|
@ -431,6 +454,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 +467,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 +501,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,40 +517,34 @@ 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,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
t.market.configTitle,
|
t.market.configTitle,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: seedTitle,
|
color: seedTitle,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
Text(
|
||||||
Text(
|
t.market.setupIntro,
|
||||||
t.market.setupIntro,
|
style: const TextStyle(
|
||||||
style: const TextStyle(color: seedMuted, fontSize: 13, height: 1.4),
|
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),
|
||||||
if (widget.location != null)
|
// Setting your area from device location is the human path;
|
||||||
Align(
|
// typing a code is the advanced fallback.
|
||||||
alignment: AlignmentDirectional.centerStart,
|
if (widget.location != null)
|
||||||
child: TextButton.icon(
|
FilledButton.tonalIcon(
|
||||||
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,26 +553,85 @@ 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)
|
||||||
|
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)
|
const SizedBox(height: 20),
|
||||||
Padding(
|
FilledButton(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
key: const Key('market.save'),
|
||||||
child: Text(
|
onPressed: _save,
|
||||||
_locationError!,
|
child: Text(t.market.save),
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class ProfileScreen extends StatefulWidget {
|
||||||
class _ProfileScreenState extends State<ProfileScreen> {
|
class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
final _name = TextEditingController();
|
final _name = TextEditingController();
|
||||||
final _about = TextEditingController();
|
final _about = TextEditingController();
|
||||||
|
final _g1 = TextEditingController();
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
bool _saving = false;
|
bool _saving = false;
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
_name.text = await widget.profileStore.name();
|
_name.text = await widget.profileStore.name();
|
||||||
_about.text = await widget.profileStore.about();
|
_about.text = await widget.profileStore.about();
|
||||||
|
_g1.text = await widget.profileStore.g1();
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +59,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
final t = context.t;
|
final t = context.t;
|
||||||
setState(() => _saving = true);
|
setState(() => _saving = true);
|
||||||
await widget.profileStore.save(name: _name.text, about: _about.text);
|
await widget.profileStore
|
||||||
|
.save(name: _name.text, about: _about.text, g1: _g1.text);
|
||||||
|
|
||||||
// Best-effort publish to the network so peers see the name.
|
// Best-effort publish to the network so peers see the name.
|
||||||
final relays = await widget.settings.relayUrls();
|
final relays = await widget.settings.relayUrls();
|
||||||
|
|
@ -67,6 +70,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
await session.profile.publish(
|
await session.profile.publish(
|
||||||
name: _name.text.trim(),
|
name: _name.text.trim(),
|
||||||
about: _about.text.trim(),
|
about: _about.text.trim(),
|
||||||
|
g1: _g1.text.trim(),
|
||||||
);
|
);
|
||||||
await session.close();
|
await session.close();
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
|
@ -82,6 +86,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_name.dispose();
|
_name.dispose();
|
||||||
_about.dispose();
|
_about.dispose();
|
||||||
|
_g1.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,6 +121,16 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
helperText: t.profile.aboutHint,
|
helperText: t.profile.aboutHint,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
key: const Key('profile.g1'),
|
||||||
|
controller: _g1,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: t.profile.g1,
|
||||||
|
helperText: t.profile.g1Hint,
|
||||||
|
helperMaxLines: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
key: const Key('profile.save'),
|
key: const Key('profile.save'),
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ Widget _wrapMarket(SocialService social, SocialSettings settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('offline (no transport) shows the "set up sharing" prompt',
|
testWidgets('offline (no transport) shows the "can\'t reach servers" state',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
final cubit = OffersCubit(null);
|
final cubit = OffersCubit(null);
|
||||||
addTearDown(cubit.close);
|
addTearDown(cubit.close);
|
||||||
|
|
@ -74,8 +74,8 @@ void main() {
|
||||||
await tester.pumpWidget(_wrapBody(cubit));
|
await tester.pumpWidget(_wrapBody(cubit));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text("Sharing isn't set up yet"), findsOneWidget);
|
expect(find.textContaining("Can't reach the community servers"),
|
||||||
expect(find.text('Set up sharing'), findsOneWidget);
|
findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('MarketScreen with no relays configured degrades to offline',
|
testWidgets('MarketScreen with no relays configured degrades to offline',
|
||||||
|
|
@ -88,7 +88,7 @@ void main() {
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text('Seeds near you'), findsOneWidget); // app bar title
|
expect(find.text('Seeds near you'), findsOneWidget); // app bar title
|
||||||
expect(find.text('Set up sharing'), findsOneWidget); // offline prompt
|
expect(find.text('Retry'), findsOneWidget); // can't-reach state offers retry
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('"use my location" fills the area with a coarse geohash',
|
testWidgets('"use my location" fills the area with a coarse geohash',
|
||||||
|
|
@ -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]+$')));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ class NostrProfileTransport implements ProfileTransport {
|
||||||
String name = '',
|
String name = '',
|
||||||
String about = '',
|
String about = '',
|
||||||
String picture = '',
|
String picture = '',
|
||||||
|
String g1 = '',
|
||||||
}) async {
|
}) async {
|
||||||
final content = jsonEncode({
|
final content = jsonEncode({
|
||||||
if (name.isNotEmpty) 'name': name,
|
if (name.isNotEmpty) 'name': name,
|
||||||
if (about.isNotEmpty) 'about': about,
|
if (about.isNotEmpty) 'about': about,
|
||||||
if (picture.isNotEmpty) 'picture': picture,
|
if (picture.isNotEmpty) 'picture': picture,
|
||||||
|
if (g1.isNotEmpty) 'g1': g1,
|
||||||
});
|
});
|
||||||
final event = Event.from(
|
final event = Event.from(
|
||||||
kind: kindMetadata,
|
kind: kindMetadata,
|
||||||
|
|
@ -50,6 +52,7 @@ class NostrProfileTransport implements ProfileTransport {
|
||||||
name: (map['name'] as String?) ?? '',
|
name: (map['name'] as String?) ?? '',
|
||||||
about: (map['about'] as String?) ?? '',
|
about: (map['about'] as String?) ?? '',
|
||||||
picture: (map['picture'] as String?) ?? '',
|
picture: (map['picture'] as String?) ?? '',
|
||||||
|
g1: (map['g1'] as String?) ?? '',
|
||||||
);
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return UserProfile(pubkeyHex: pubkeyHex);
|
return UserProfile(pubkeyHex: pubkeyHex);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ class UserProfile {
|
||||||
this.name = '',
|
this.name = '',
|
||||||
this.about = '',
|
this.about = '',
|
||||||
this.picture = '',
|
this.picture = '',
|
||||||
|
this.g1 = '',
|
||||||
});
|
});
|
||||||
|
|
||||||
final String pubkeyHex;
|
final String pubkeyHex;
|
||||||
|
|
@ -13,7 +14,12 @@ class UserProfile {
|
||||||
final String about;
|
final String about;
|
||||||
final String picture;
|
final String picture;
|
||||||
|
|
||||||
bool get isEmpty => name.isEmpty && about.isEmpty && picture.isEmpty;
|
/// Optional Ğ1 (Duniter) address the person chose to publish, so peers can pay
|
||||||
|
/// them in Ğ1. Separate from the Nostr key; empty when not shared.
|
||||||
|
final String g1;
|
||||||
|
|
||||||
|
bool get isEmpty =>
|
||||||
|
name.isEmpty && about.isEmpty && picture.isEmpty && g1.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Publish/fetch public profile metadata. Kind:0 is replaceable — publishing
|
/// Publish/fetch public profile metadata. Kind:0 is replaceable — publishing
|
||||||
|
|
@ -21,7 +27,7 @@ class UserProfile {
|
||||||
/// sibling of the offer/message/trust transports over the same connection.
|
/// sibling of the offer/message/trust transports over the same connection.
|
||||||
abstract interface class ProfileTransport {
|
abstract interface class ProfileTransport {
|
||||||
/// Publishes (replaces) this identity's profile.
|
/// Publishes (replaces) this identity's profile.
|
||||||
Future<void> publish({String name, String about, String picture});
|
Future<void> publish({String name, String about, String picture, String g1});
|
||||||
|
|
||||||
/// Fetches [pubkeyHex]'s latest profile, or null if they never published one.
|
/// Fetches [pubkeyHex]'s latest profile, or null if they never published one.
|
||||||
Future<UserProfile?> fetch(String pubkeyHex);
|
Future<UserProfile?> fetch(String pubkeyHex);
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,11 @@ void main() {
|
||||||
test('publish then fetch round-trips a profile (kind:0)', () async {
|
test('publish then fetch round-trips a profile (kind:0)', () async {
|
||||||
final alice = await idFor(1);
|
final alice = await idFor(1);
|
||||||
final aliceConn = await NostrConnection.connect(relay.url, identity: alice);
|
final aliceConn = await NostrConnection.connect(relay.url, identity: alice);
|
||||||
await NostrProfileTransport(aliceConn)
|
await NostrProfileTransport(aliceConn).publish(
|
||||||
.publish(name: 'Alicia', about: 'Guardo tomate rosa');
|
name: 'Alicia',
|
||||||
|
about: 'Guardo tomate rosa',
|
||||||
|
g1: 'G1abc123',
|
||||||
|
);
|
||||||
|
|
||||||
// Anyone else can fetch it.
|
// Anyone else can fetch it.
|
||||||
final bob = await idFor(2);
|
final bob = await idFor(2);
|
||||||
|
|
@ -28,6 +31,7 @@ void main() {
|
||||||
expect(fetched, isNotNull);
|
expect(fetched, isNotNull);
|
||||||
expect(fetched!.name, 'Alicia');
|
expect(fetched!.name, 'Alicia');
|
||||||
expect(fetched.about, 'Guardo tomate rosa');
|
expect(fetched.about, 'Guardo tomate rosa');
|
||||||
|
expect(fetched.g1, 'G1abc123');
|
||||||
|
|
||||||
await aliceConn.close();
|
await aliceConn.close();
|
||||||
await bobConn.close();
|
await bobConn.close();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue