feat(chat): fee floor, real delivery status, pay-from-funded, funds pre-check

Chat sends move 0 value, so the network fee is structurally load-bearing (it's
the only thing that forces a real shielded input; 0-value + 0-fee builds a
degenerate, unrelayable tx). Three gaps addressed:

1. Fee floor — broadcastChatMemos now uses max(getDefaultFee(), kChatMinFeeDrgx),
   so a 0 / too-low global default-fee setting can't silently break chat.

2. Real delivery status — the echo was marked Sent on SUBMIT regardless of the
   on-chain outcome (the z_sendmany callback was empty), so failures were
   invisible and the Retry affordance never fired for async failures. Add a third
   ChatDelivery::Sending state (appended so persisted 0=Sent stays valid); record
   the echo in-memory as Sending, and resolve it to Sent/Failed from the
   z_sendmany completion callback — persisting only the final status (so a restart
   never shows a stuck spinner; a stray persisted Sending loads as Sent). A subtle
   "sending…" label shows while in flight.

3. Pay-from-funded + pre-check — z_sendmany spends from one z-address, and the
   identity reply address may be unfunded while funds sit elsewhere. chatPayFromZaddr
   picks a spendable z-address that can cover the fee (preferring the identity
   address); the memo still advertises the identity address as reply-to, so paying
   from a different note is transport-transparent. If nothing can cover the fee, a
   clear "need a small shielded balance" toast replaces the cryptic failure.

Full node only for the callback path; lite resolves optimistically on queue.
8-language strings + CJK subset (+1 glyph 賄).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:27:43 -05:00
parent c291e8a587
commit ef247c95ff
19 changed files with 133 additions and 22 deletions

Binary file not shown.

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "Wähle eine Unterhaltung aus, um sie anzuzeigen.",
"chat_send": "Senden",
"chat_send_failed": "nicht gesendet",
"chat_sending": "senden…",
"chat_time_now": "jetzt",
"chat_toast_compose_failed": "Nachricht konnte nicht erstellt werden (zu lang?).",
"chat_toast_lite_busy": "Es wird bereits gesendet, oder es ist keine Wallet geöffnet.",
"chat_toast_need_funds": "Ein kleines geschütztes Guthaben ist nötig, um Chats zu senden (zur Deckung der Gebühr).",
"chat_toast_no_zaddr": "Keine z-Adresse verfügbar, um den Chat zu senden.",
"chat_toast_not_connected": "Nicht verbunden Nachricht nicht gesendet.",
"chat_toast_request_compose_failed": "Kontaktanfrage konnte nicht erstellt werden (ungültige Adresse / ungültiger Text?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "Selecciona una conversación para verla.",
"chat_send": "Enviar",
"chat_send_failed": "no enviado",
"chat_sending": "enviando…",
"chat_time_now": "ahora",
"chat_toast_compose_failed": "No se pudo componer el mensaje (¿demasiado largo?).",
"chat_toast_lite_busy": "Ya hay un envío en curso, o no hay ningún monedero abierto.",
"chat_toast_need_funds": "Necesitas un pequeño saldo blindado para enviar chats (para cubrir la comisión).",
"chat_toast_no_zaddr": "No hay ninguna dirección z disponible desde la que enviar el chat.",
"chat_toast_not_connected": "Sin conexión: mensaje de chat no enviado.",
"chat_toast_request_compose_failed": "No se pudo componer la solicitud de contacto (¿dirección o texto no válidos?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "Sélectionnez une conversation pour l'afficher.",
"chat_send": "Envoyer",
"chat_send_failed": "non envoyé",
"chat_sending": "envoi…",
"chat_time_now": "à l'instant",
"chat_toast_compose_failed": "Impossible de composer le message (trop long ?).",
"chat_toast_lite_busy": "Un envoi est déjà en cours, ou aucun portefeuille n'est ouvert.",
"chat_toast_need_funds": "Un petit solde blindé est nécessaire pour envoyer des messages (pour couvrir les frais).",
"chat_toast_no_zaddr": "Aucune adresse Z disponible pour envoyer le message.",
"chat_toast_not_connected": "Non connecté — message non envoyé.",
"chat_toast_request_compose_failed": "Impossible de composer la demande de contact (adresse / texte invalide ?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "表示する会話を選択してください。",
"chat_send": "送信",
"chat_send_failed": "未送信",
"chat_sending": "送信中…",
"chat_time_now": "たった今",
"chat_toast_compose_failed": "メッセージを作成できませんでした(長すぎませんか?)。",
"chat_toast_lite_busy": "すでに送信処理が進行中か、ウォレットが開かれていません。",
"chat_toast_need_funds": "チャットを送信するには、手数料を賄うための少額のシールド残高が必要です。",
"chat_toast_no_zaddr": "送信元に使えるZアドレスがありません。",
"chat_toast_not_connected": "未接続 — チャットメッセージは送信されませんでした。",
"chat_toast_request_compose_failed": "連絡リクエストを作成できませんでした(アドレスまたはテキストが無効?)。",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "볼 대화를 선택하세요.",
"chat_send": "전송",
"chat_send_failed": "전송 안 됨",
"chat_sending": "전송 중…",
"chat_time_now": "방금",
"chat_toast_compose_failed": "메시지를 작성할 수 없습니다 (너무 긴가요?).",
"chat_toast_lite_busy": "이미 전송이 진행 중이거나 열린 지갑이 없습니다.",
"chat_toast_need_funds": "채팅을 보내려면 수수료를 낼 소액의 보호 잔액이 필요합니다.",
"chat_toast_no_zaddr": "채팅을 보낼 z-주소가 없습니다.",
"chat_toast_not_connected": "연결되지 않음 — 채팅 메시지가 전송되지 않았습니다.",
"chat_toast_request_compose_failed": "연락 요청을 작성할 수 없습니다 (잘못된 주소 / 텍스트?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "Selecione uma conversa para visualizá-la.",
"chat_send": "Enviar",
"chat_send_failed": "não enviada",
"chat_sending": "enviando…",
"chat_time_now": "agora",
"chat_toast_compose_failed": "Não foi possível compor a mensagem (muito longa?).",
"chat_toast_lite_busy": "Já há um envio em andamento, ou nenhuma carteira está aberta.",
"chat_toast_need_funds": "É necessário um pequeno saldo blindado para enviar chats (para cobrir a taxa).",
"chat_toast_no_zaddr": "Nenhum endereço-z disponível para enviar o chat.",
"chat_toast_not_connected": "Não conectado — mensagem de chat não enviada.",
"chat_toast_request_compose_failed": "Não foi possível compor a solicitação de contato (endereço / texto inválido?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "Выберите переписку для просмотра.",
"chat_send": "Отправить",
"chat_send_failed": "не отправлено",
"chat_sending": "отправка…",
"chat_time_now": "сейчас",
"chat_toast_compose_failed": "Не удалось составить сообщение (слишком длинное?).",
"chat_toast_lite_busy": "Отправка уже выполняется, или кошелёк не открыт.",
"chat_toast_need_funds": "Для отправки сообщений нужен небольшой экранированный баланс (для оплаты комиссии).",
"chat_toast_no_zaddr": "Нет доступного Z-адреса для отправки сообщений.",
"chat_toast_not_connected": "Нет подключения — сообщение не отправлено.",
"chat_toast_request_compose_failed": "Не удалось составить запрос контакта (неверный адрес / текст?).",

View File

@@ -162,9 +162,11 @@
"chat_select_hint": "选择一个对话以查看。",
"chat_send": "发送",
"chat_send_failed": "未发送",
"chat_sending": "发送中…",
"chat_time_now": "刚刚",
"chat_toast_compose_failed": "无法编写该消息(内容过长?)。",
"chat_toast_lite_busy": "已有发送正在进行中,或未打开任何钱包。",
"chat_toast_need_funds": "发送聊天需要少量屏蔽余额(用于支付手续费)。",
"chat_toast_no_zaddr": "没有可用于发送聊天的 z 地址。",
"chat_toast_not_connected": "未连接——聊天消息未发送。",
"chat_toast_request_compose_failed": "无法编写联系人请求(地址或文本无效?)。",