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 TranslationsEs extends Translations with BaseTranslations<AppLocale, Trans
@override late final _Translations$desiccant$es desiccant = _Translations$desiccant$es._(_root);
@override late final _Translations$unit$es unit = _Translations$unit$es._(_root);
@override late final _Translations$market$es market = _Translations$market$es._(_root);
@override late final _Translations$chat$es chat = _Translations$chat$es._(_root);
}
// Path: app
@ -612,6 +613,7 @@ class _Translations$market$es extends Translations$market$en {
@override String get searching => 'Buscando por tu zona…';
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
@override String get near => 'Cerca de ti';
@override String get contact => 'Mensaje';
@override String get configTitle => 'Configuración de compartir';
@override String get setupIntro => 'Compartir con gente cercana es opcional. Para activarlo, indica tu zona aproximada y únete a una comunidad de confianza — una red de semillas o un colectivo que mantiene un punto de encuentro compartido para pasar mensajes. Sin ninguna empresa en medio.';
@override String get areaLabel => 'Tu zona';
@ -630,6 +632,20 @@ class _Translations$market$es extends Translations$market$en {
@override String get queued => 'Guardado — las compartiremos cuando tengas conexión';
}
// Path: chat
class _Translations$chat$es extends Translations$chat$en {
_Translations$chat$es._(TranslationsEs root) : this._root = root, super.internal(root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Chat';
@override String get hint => 'Escribe un mensaje…';
@override String get send => 'Enviar';
@override String get empty => 'Aún no hay mensajes — saluda';
@override String get offline => 'Configura el compartir para enviar mensajes';
}
// Path: intro.slides
class _Translations$intro$slides$es extends Translations$intro$slides$en {
_Translations$intro$slides$es._(TranslationsEs root) : this._root = root, super.internal(root);
@ -1275,6 +1291,7 @@ extension on TranslationsEs {
'market.searching' => 'Buscando por tu zona…',
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
'market.near' => 'Cerca de ti',
'market.contact' => 'Mensaje',
'market.configTitle' => 'Configuración de compartir',
'market.setupIntro' => 'Compartir con gente cercana es opcional. Para activarlo, indica tu zona aproximada y únete a una comunidad de confianza — una red de semillas o un colectivo que mantiene un punto de encuentro compartido para pasar mensajes. Sin ninguna empresa en medio.',
'market.areaLabel' => 'Tu zona',
@ -1291,6 +1308,11 @@ extension on TranslationsEs {
'market.useLocation' => 'Usar mi ubicación aproximada',
'market.locationFailed' => 'No se pudo obtener tu ubicación',
'market.queued' => 'Guardado — las compartiremos cuando tengas conexión',
'chat.title' => 'Chat',
'chat.hint' => 'Escribe un mensaje…',
'chat.send' => 'Enviar',
'chat.empty' => 'Aún no hay mensajes — saluda',
'chat.offline' => 'Configura el compartir para enviar mensajes',
_ => null,
};
}