feat(block2): messaging UI — private 1:1 chat (NIP-17)

- MessagesCubit: 1:1 conversation over MessageTransport. Filters the shared
  inbox to the peer; tags our own sends with our pubkey (NIP-17 wraps don't come
  back to the sender) so the UI can align bubbles; degrades gracefully offline.
- ChatScreen (mockup 10): message bubbles (mine right / peer left) + a composer;
  opens a session lazily, shows 'set up sharing' when there's no relay.
- Entry point: a 'Message' button on each offer card (hidden on your own
  listings) → /chat/:pubkey.
- Routing + i18n en/es/pt.

Tests: 5 cubit (send/receive/order/filter/offline) + chat offline + market, green.
This commit is contained in:
vjrj 2026-07-10 10:59:47 +02:00
parent 0dd7c28039
commit efd64c4da4
13 changed files with 627 additions and 9 deletions

View file

@ -69,6 +69,7 @@ class TranslationsPt extends Translations with BaseTranslations<AppLocale, Trans
@override late final _Translations$desiccant$pt desiccant = _Translations$desiccant$pt._(_root);
@override late final _Translations$unit$pt unit = _Translations$unit$pt._(_root);
@override late final _Translations$market$pt market = _Translations$market$pt._(_root);
@override late final _Translations$chat$pt chat = _Translations$chat$pt._(_root);
}
// Path: app
@ -608,6 +609,7 @@ class _Translations$market$pt extends Translations$market$en {
@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 near => 'Perto de ti';
@override String get contact => 'Mensagem';
@override String get configTitle => 'Configuração de partilha';
@override String get setupIntro => 'Partilhar com pessoas por perto é opcional. Para ativar, indica a tua zona aproximada e junta-te a uma comunidade de confiança — uma rede de sementes ou um coletivo que mantém um ponto de encontro partilhado para passar mensagens. Sem nenhuma empresa no meio.';
@override String get areaLabel => 'A tua zona';
@ -626,6 +628,20 @@ class _Translations$market$pt extends Translations$market$en {
@override String get queued => 'Guardado — vamos partilhá-las quando tiveres ligação';
}
// Path: chat
class _Translations$chat$pt extends Translations$chat$en {
_Translations$chat$pt._(TranslationsPt root) : this._root = root, super.internal(root);
final TranslationsPt _root; // ignore: unused_field
// Translations
@override String get title => 'Conversa';
@override String get hint => 'Escreve uma mensagem…';
@override String get send => 'Enviar';
@override String get empty => 'Ainda não há mensagens — diz olá';
@override String get offline => 'Configura a partilha para enviar mensagens';
}
// Path: intro.slides
class _Translations$intro$slides$pt extends Translations$intro$slides$en {
_Translations$intro$slides$pt._(TranslationsPt root) : this._root = root, super.internal(root);
@ -1267,6 +1283,7 @@ extension on TranslationsPt {
'market.searching' => 'A procurar pela tua zona…',
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
'market.near' => 'Perto de ti',
'market.contact' => 'Mensagem',
'market.configTitle' => 'Configuração de partilha',
'market.setupIntro' => 'Partilhar com pessoas por perto é opcional. Para ativar, indica a tua zona aproximada e junta-te a uma comunidade de confiança — uma rede de sementes ou um coletivo que mantém um ponto de encontro partilhado para passar mensagens. Sem nenhuma empresa no meio.',
'market.areaLabel' => 'A tua zona',
@ -1283,6 +1300,11 @@ extension on TranslationsPt {
'market.useLocation' => 'Usar a minha localização aproximada',
'market.locationFailed' => 'Não foi possível obter a tua localização',
'market.queued' => 'Guardado — vamos partilhá-las quando tiveres ligação',
'chat.title' => 'Conversa',
'chat.hint' => 'Escreve uma mensagem…',
'chat.send' => 'Enviar',
'chat.empty' => 'Ainda não há mensagens — diz olá',
'chat.offline' => 'Configura a partilha para enviar mensagens',
_ => null,
};
}