Compare commits
12 Commits
f6024557d5
...
1a41dec8d8
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a41dec8d8 | |||
| 88e10f3e06 | |||
| d29283a9b7 | |||
| ca65aa8bf7 | |||
| dae677acd8 | |||
| 4ef09d5546 | |||
| 21be39c725 | |||
| abbdf3f4f7 | |||
| 28288d7197 | |||
| 304f65c3b4 | |||
| 04a5078133 | |||
| b6b4fdacb5 |
Binary file not shown.
@@ -55,6 +55,17 @@
|
||||
"amount_label": "Betrag:",
|
||||
"appearance": "ERSCHEINUNGSBILD",
|
||||
"auto_shield": "Mining automatisch abschirmen",
|
||||
"av_intro": "Mining-Software wird oft als potenziell unerwünscht eingestuft. Führen Sie diese Schritte aus, um das Pool-Mining zu aktivieren:",
|
||||
"av_open_security": "Windows-Sicherheit öffnen",
|
||||
"av_step1": "Schritt 1: Ausnahme hinzufügen",
|
||||
"av_step1_b1": "Öffnen Sie Windows-Sicherheit > Viren- & Bedrohungsschutz",
|
||||
"av_step1_b2": "Klicken Sie auf Einstellungen verwalten > Ausschlüsse > Hinzufügen oder entfernen",
|
||||
"av_step1_b3": "Ordner hinzufügen: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "Schritt 2: Aus Quarantäne wiederherstellen (falls nötig)",
|
||||
"av_step2_b1": "Windows-Sicherheit > Schutzverlauf",
|
||||
"av_step2_b2": "Suchen Sie xmrig.exe und klicken Sie auf Wiederherstellen",
|
||||
"av_step3": "Schritt 3: Wallet neu starten und erneut versuchen",
|
||||
"av_title": "Windows Defender hat den Miner blockiert",
|
||||
"available": "Verfügbar",
|
||||
"backup_backing_up": "Sicherung läuft...",
|
||||
"backup_create": "Sicherung erstellen",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "Mempool",
|
||||
"explorer_mempool_size": "Größe",
|
||||
"explorer_mempool_txs": "Transaktionen",
|
||||
"explorer_next_block": "Nächster Block",
|
||||
"explorer_no_results": "Keine passenden zwischengespeicherten Blöcke",
|
||||
"explorer_not_connected": "Nicht mit dem Daemon verbunden — Block- oder Transaktions-Hash kann nicht nachgeschlagen werden",
|
||||
"explorer_prev_block": "Vorheriger Block",
|
||||
"explorer_recent_blocks": "Letzte Blöcke",
|
||||
"explorer_search": "Suchen",
|
||||
"explorer_section": "EXPLORER",
|
||||
"explorer_tx_outputs": "Ausgaben",
|
||||
"explorer_tx_size": "Größe",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "Exportieren",
|
||||
"export_csv": "CSV exportieren",
|
||||
"export_keys_btn": "Schlüssel exportieren",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "Betrag",
|
||||
"send_amount_details": "BETRAGSDETAILS",
|
||||
"send_amount_upper": "BETRAG",
|
||||
"send_cannot_send_now": "Senden derzeit nicht möglich — prüfen Sie Verbindung, Synchronisierung und verfügbares Guthaben.",
|
||||
"send_clear_fields": "Alle Formularfelder leeren?",
|
||||
"send_contacts_button": "Aus Kontakten auswählen",
|
||||
"send_copy_error": "Fehler kopieren",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "Vorgang läuft...",
|
||||
"shield_max_utxos": "Max. UTXOs pro Vorgang",
|
||||
"shield_merge_done": "Abschirmung/Zusammenführung abgeschlossen!",
|
||||
"shield_no_zaddr_hint": "Noch keine geschützte (z-)Adresse — erstellen Sie zuerst eine auf der Registerkarte Empfangen.",
|
||||
"shield_op_failed": "Vorgang fehlgeschlagen: ",
|
||||
"shield_op_submitted": "Vorgang übermittelt: ",
|
||||
"shield_operation_id": "Vorgangs-ID: %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "Cantidad:",
|
||||
"appearance": "APARIENCIA",
|
||||
"auto_shield": "Auto-proteger minería",
|
||||
"av_intro": "El software de minería suele marcarse como potencialmente no deseado. Sigue estos pasos para habilitar la minería en pool:",
|
||||
"av_open_security": "Abrir Seguridad de Windows",
|
||||
"av_step1": "Paso 1: Agregar exclusión",
|
||||
"av_step1_b1": "Abre Seguridad de Windows > Protección antivirus y contra amenazas",
|
||||
"av_step1_b2": "Haz clic en Administrar configuración > Exclusiones > Agregar o quitar",
|
||||
"av_step1_b3": "Agrega la carpeta: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "Paso 2: Restaurar desde cuarentena (si es necesario)",
|
||||
"av_step2_b1": "Seguridad de Windows > Historial de protección",
|
||||
"av_step2_b2": "Busca xmrig.exe y haz clic en Restaurar",
|
||||
"av_step3": "Paso 3: Reinicia el monedero e inténtalo de nuevo",
|
||||
"av_title": "Windows Defender bloqueó el minero",
|
||||
"available": "Disponible",
|
||||
"backup_backing_up": "Respaldando...",
|
||||
"backup_create": "Crear Respaldo",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "Mempool",
|
||||
"explorer_mempool_size": "Tamaño",
|
||||
"explorer_mempool_txs": "Transacciones",
|
||||
"explorer_next_block": "Bloque siguiente",
|
||||
"explorer_no_results": "No hay bloques en caché que coincidan",
|
||||
"explorer_not_connected": "Sin conexión al daemon: no se puede buscar un bloque ni un hash de transacción",
|
||||
"explorer_prev_block": "Bloque anterior",
|
||||
"explorer_recent_blocks": "Bloques Recientes",
|
||||
"explorer_search": "Buscar",
|
||||
"explorer_section": "EXPLORADOR",
|
||||
"explorer_tx_outputs": "Salidas",
|
||||
"explorer_tx_size": "Tamaño",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "Exportar",
|
||||
"export_csv": "Exportar CSV",
|
||||
"export_keys_btn": "Exportar Claves",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "Cantidad",
|
||||
"send_amount_details": "DETALLES DE CANTIDAD",
|
||||
"send_amount_upper": "CANTIDAD",
|
||||
"send_cannot_send_now": "No se puede enviar ahora — revisa la conexión, la sincronización y el saldo disponible.",
|
||||
"send_clear_fields": "¿Limpiar todos los campos del formulario?",
|
||||
"send_contacts_button": "Elegir de contactos",
|
||||
"send_copy_error": "Copiar Error",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "Operación en progreso...",
|
||||
"shield_max_utxos": "UTXOs máximos por operación",
|
||||
"shield_merge_done": "¡Protección/fusión completada!",
|
||||
"shield_no_zaddr_hint": "Aún no hay dirección blindada (z) — crea una primero en la pestaña Recibir.",
|
||||
"shield_op_failed": "Operación fallida: ",
|
||||
"shield_op_submitted": "Operación enviada: ",
|
||||
"shield_operation_id": "ID de operación: %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "Montant :",
|
||||
"appearance": "APPARENCE",
|
||||
"auto_shield": "Auto-blindage du minage",
|
||||
"av_intro": "Les logiciels de minage sont souvent signalés comme potentiellement indésirables. Suivez ces étapes pour activer le minage en pool :",
|
||||
"av_open_security": "Ouvrir Sécurité Windows",
|
||||
"av_step1": "Étape 1 : Ajouter une exclusion",
|
||||
"av_step1_b1": "Ouvrez Sécurité Windows > Protection contre les virus et menaces",
|
||||
"av_step1_b2": "Cliquez sur Gérer les paramètres > Exclusions > Ajouter ou supprimer",
|
||||
"av_step1_b3": "Ajoutez le dossier : %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "Étape 2 : Restaurer depuis la quarantaine (si nécessaire)",
|
||||
"av_step2_b1": "Sécurité Windows > Historique de protection",
|
||||
"av_step2_b2": "Trouvez xmrig.exe et cliquez sur Restaurer",
|
||||
"av_step3": "Étape 3 : Redémarrez le portefeuille et réessayez",
|
||||
"av_title": "Windows Defender a bloqué le mineur",
|
||||
"available": "Disponible",
|
||||
"backup_backing_up": "Sauvegarde en cours...",
|
||||
"backup_create": "Créer une sauvegarde",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "Mempool",
|
||||
"explorer_mempool_size": "Taille",
|
||||
"explorer_mempool_txs": "Transactions",
|
||||
"explorer_next_block": "Bloc suivant",
|
||||
"explorer_no_results": "Aucun bloc en cache correspondant",
|
||||
"explorer_not_connected": "Non connecté au daemon — impossible de rechercher un hash de bloc ou de transaction",
|
||||
"explorer_prev_block": "Bloc précédent",
|
||||
"explorer_recent_blocks": "Blocs récents",
|
||||
"explorer_search": "Rechercher",
|
||||
"explorer_section": "EXPLORATEUR",
|
||||
"explorer_tx_outputs": "Sorties",
|
||||
"explorer_tx_size": "Taille",
|
||||
"explorer_txid": "TxID :",
|
||||
"export": "Exporter",
|
||||
"export_csv": "Exporter en CSV",
|
||||
"export_keys_btn": "Exporter les clés",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "Montant",
|
||||
"send_amount_details": "DÉTAILS DU MONTANT",
|
||||
"send_amount_upper": "MONTANT",
|
||||
"send_cannot_send_now": "Envoi impossible pour le moment — vérifiez la connexion, la synchronisation et le solde disponible.",
|
||||
"send_clear_fields": "Effacer tous les champs du formulaire ?",
|
||||
"send_contacts_button": "Choisir parmi les contacts",
|
||||
"send_copy_error": "Copier l'erreur",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "Opération en cours...",
|
||||
"shield_max_utxos": "UTXOs max par opération",
|
||||
"shield_merge_done": "Blindage/fusion terminé !",
|
||||
"shield_no_zaddr_hint": "Aucune adresse blindée (z) pour l'instant — créez-en une d'abord dans l'onglet Recevoir.",
|
||||
"shield_op_failed": "Échec de l'opération : ",
|
||||
"shield_op_submitted": "Opération soumise : ",
|
||||
"shield_operation_id": "ID d'opération : %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "金額:",
|
||||
"appearance": "外観",
|
||||
"auto_shield": "マイニング自動シールド",
|
||||
"av_intro": "マイニングソフトウェアは、望ましくない可能性があるものとしてフラグが立てられることがよくあります。プールマイニングを有効にするには、次の手順に従ってください。",
|
||||
"av_open_security": "Windows セキュリティを開く",
|
||||
"av_step1": "ステップ1: 除外の追加",
|
||||
"av_step1_b1": "Windows セキュリティ > ウイルスと脅威の防止 を開く",
|
||||
"av_step1_b2": "設定の管理 > 除外 > 追加または削除 をクリック",
|
||||
"av_step1_b3": "フォルダーを追加: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "ステップ2: 検疫から復元(必要な場合)",
|
||||
"av_step2_b1": "Windows セキュリティ > 保護の履歴",
|
||||
"av_step2_b2": "xmrig.exe を見つけて「復元」をクリック",
|
||||
"av_step3": "ステップ3: ウォレットを再起動して再試行",
|
||||
"av_title": "Windows Defender がマイナーをブロックしました",
|
||||
"available": "利用可能",
|
||||
"backup_backing_up": "バックアップ中...",
|
||||
"backup_create": "バックアップを作成",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "メモリプール",
|
||||
"explorer_mempool_size": "サイズ",
|
||||
"explorer_mempool_txs": "トランザクション",
|
||||
"explorer_next_block": "次のブロック",
|
||||
"explorer_no_results": "一致するキャッシュ済みブロックがありません",
|
||||
"explorer_not_connected": "デーモンに未接続 — ブロックまたはトランザクションハッシュを検索できません",
|
||||
"explorer_prev_block": "前のブロック",
|
||||
"explorer_recent_blocks": "最近のブロック",
|
||||
"explorer_search": "検索",
|
||||
"explorer_section": "エクスプローラー",
|
||||
"explorer_tx_outputs": "出力",
|
||||
"explorer_tx_size": "サイズ",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "エクスポート",
|
||||
"export_csv": "CSVエクスポート",
|
||||
"export_keys_btn": "鍵をエクスポート",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "金額",
|
||||
"send_amount_details": "金額の詳細",
|
||||
"send_amount_upper": "金額",
|
||||
"send_cannot_send_now": "現在送信できません — 接続、同期、利用可能な残高を確認してください。",
|
||||
"send_clear_fields": "すべてのフォームフィールドをクリアしますか?",
|
||||
"send_contacts_button": "連絡先から選択",
|
||||
"send_copy_error": "エラーをコピー",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "操作進行中...",
|
||||
"shield_max_utxos": "1回の操作あたりの最大UTXO数",
|
||||
"shield_merge_done": "シールド/統合が完了しました!",
|
||||
"shield_no_zaddr_hint": "シールドアドレス(z)がまだありません — まず「受取」タブで作成してください。",
|
||||
"shield_op_failed": "操作に失敗しました: ",
|
||||
"shield_op_submitted": "操作を送信しました: ",
|
||||
"shield_operation_id": "オペレーション ID:%s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "금액:",
|
||||
"appearance": "외관",
|
||||
"auto_shield": "채굴 자동 차폐",
|
||||
"av_intro": "채굴 소프트웨어는 종종 잠재적으로 원치 않는 항목으로 표시됩니다. 풀 채굴을 활성화하려면 다음 단계를 따르세요:",
|
||||
"av_open_security": "Windows 보안 열기",
|
||||
"av_step1": "1단계: 제외 추가",
|
||||
"av_step1_b1": "Windows 보안 > 바이러스 및 위협 방지 열기",
|
||||
"av_step1_b2": "설정 관리 > 제외 > 추가 또는 제거 클릭",
|
||||
"av_step1_b3": "폴더 추가: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "2단계: 격리에서 복원(필요한 경우)",
|
||||
"av_step2_b1": "Windows 보안 > 보호 기록",
|
||||
"av_step2_b2": "xmrig.exe를 찾아 복원 클릭",
|
||||
"av_step3": "3단계: 지갑을 다시 시작하고 다시 시도",
|
||||
"av_title": "Windows Defender가 채굴기를 차단했습니다",
|
||||
"available": "사용 가능",
|
||||
"backup_backing_up": "백업 중...",
|
||||
"backup_create": "백업 생성",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "멤풀",
|
||||
"explorer_mempool_size": "크기",
|
||||
"explorer_mempool_txs": "트랜잭션",
|
||||
"explorer_next_block": "다음 블록",
|
||||
"explorer_no_results": "일치하는 캐시된 블록이 없습니다",
|
||||
"explorer_not_connected": "데몬에 연결되지 않음 — 블록 또는 트랜잭션 해시를 조회할 수 없습니다",
|
||||
"explorer_prev_block": "이전 블록",
|
||||
"explorer_recent_blocks": "최근 블록",
|
||||
"explorer_search": "검색",
|
||||
"explorer_section": "탐색기",
|
||||
"explorer_tx_outputs": "출력",
|
||||
"explorer_tx_size": "크기",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "내보내기",
|
||||
"export_csv": "CSV 내보내기",
|
||||
"export_keys_btn": "키 내보내기",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "금액",
|
||||
"send_amount_details": "금액 상세",
|
||||
"send_amount_upper": "금액",
|
||||
"send_cannot_send_now": "지금 보낼 수 없습니다 — 연결, 동기화, 사용 가능한 잔액을 확인하세요.",
|
||||
"send_clear_fields": "모든 양식 필드를 지우시겠습니까?",
|
||||
"send_contacts_button": "연락처에서 선택",
|
||||
"send_copy_error": "오류 복사",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "작업 진행 중...",
|
||||
"shield_max_utxos": "작업당 최대 UTXO 수",
|
||||
"shield_merge_done": "차폐/통합이 완료되었습니다!",
|
||||
"shield_no_zaddr_hint": "아직 보호된(z) 주소가 없습니다 — 먼저 받기 탭에서 만드세요.",
|
||||
"shield_op_failed": "작업 실패: ",
|
||||
"shield_op_submitted": "작업이 제출되었습니다: ",
|
||||
"shield_operation_id": "작업 ID: %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "Valor:",
|
||||
"appearance": "APARÊNCIA",
|
||||
"auto_shield": "Auto-blindar mineração",
|
||||
"av_intro": "Softwares de mineração costumam ser sinalizados como potencialmente indesejados. Siga estes passos para habilitar a mineração em pool:",
|
||||
"av_open_security": "Abrir Segurança do Windows",
|
||||
"av_step1": "Passo 1: Adicionar exclusão",
|
||||
"av_step1_b1": "Abra Segurança do Windows > Proteção contra vírus e ameaças",
|
||||
"av_step1_b2": "Clique em Gerenciar configurações > Exclusões > Adicionar ou remover",
|
||||
"av_step1_b3": "Adicione a pasta: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "Passo 2: Restaurar da quarentena (se necessário)",
|
||||
"av_step2_b1": "Segurança do Windows > Histórico de proteção",
|
||||
"av_step2_b2": "Encontre xmrig.exe e clique em Restaurar",
|
||||
"av_step3": "Passo 3: Reinicie a carteira e tente novamente",
|
||||
"av_title": "Windows Defender bloqueou o minerador",
|
||||
"available": "Disponível",
|
||||
"backup_backing_up": "Fazendo backup...",
|
||||
"backup_create": "Criar Backup",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "Mempool",
|
||||
"explorer_mempool_size": "Tamanho",
|
||||
"explorer_mempool_txs": "Transações",
|
||||
"explorer_next_block": "Próximo bloco",
|
||||
"explorer_no_results": "Nenhum bloco em cache correspondente",
|
||||
"explorer_not_connected": "Não conectado ao daemon — não é possível procurar um bloco ou hash de transação",
|
||||
"explorer_prev_block": "Bloco anterior",
|
||||
"explorer_recent_blocks": "Blocos Recentes",
|
||||
"explorer_search": "Pesquisar",
|
||||
"explorer_section": "EXPLORADOR",
|
||||
"explorer_tx_outputs": "Saídas",
|
||||
"explorer_tx_size": "Tamanho",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "Exportar",
|
||||
"export_csv": "Exportar CSV",
|
||||
"export_keys_btn": "Exportar Chaves",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "Valor",
|
||||
"send_amount_details": "DETALHES DO VALOR",
|
||||
"send_amount_upper": "VALOR",
|
||||
"send_cannot_send_now": "Não é possível enviar agora — verifique a conexão, a sincronização e o saldo disponível.",
|
||||
"send_clear_fields": "Limpar todos os campos do formulário?",
|
||||
"send_contacts_button": "Escolher dos contatos",
|
||||
"send_copy_error": "Copiar Erro",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "Operação em andamento...",
|
||||
"shield_max_utxos": "Máx. UTXOs por operação",
|
||||
"shield_merge_done": "Blindagem/fusão concluída!",
|
||||
"shield_no_zaddr_hint": "Ainda não há endereço blindado (z) — crie um primeiro na aba Receber.",
|
||||
"shield_op_failed": "Falha na operação: ",
|
||||
"shield_op_submitted": "Operação enviada: ",
|
||||
"shield_operation_id": "ID da operação: %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "Сумма:",
|
||||
"appearance": "ВНЕШНИЙ ВИД",
|
||||
"auto_shield": "Авто-экранирование майнинга",
|
||||
"av_intro": "Программы для майнинга часто помечаются как потенциально нежелательные. Выполните эти шаги, чтобы включить пул-майнинг:",
|
||||
"av_open_security": "Открыть Безопасность Windows",
|
||||
"av_step1": "Шаг 1: Добавить исключение",
|
||||
"av_step1_b1": "Откройте Безопасность Windows > Защита от вирусов и угроз",
|
||||
"av_step1_b2": "Нажмите Управление настройками > Исключения > Добавить или удалить",
|
||||
"av_step1_b3": "Добавьте папку: %APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "Шаг 2: Восстановить из карантина (при необходимости)",
|
||||
"av_step2_b1": "Безопасность Windows > Журнал защиты",
|
||||
"av_step2_b2": "Найдите xmrig.exe и нажмите Восстановить",
|
||||
"av_step3": "Шаг 3: Перезапустите кошелёк и попробуйте снова",
|
||||
"av_title": "Windows Defender заблокировал майнер",
|
||||
"available": "Доступно",
|
||||
"backup_backing_up": "Создание резервной копии...",
|
||||
"backup_create": "Создать резервную копию",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "Мемпул",
|
||||
"explorer_mempool_size": "Размер",
|
||||
"explorer_mempool_txs": "Транзакции",
|
||||
"explorer_next_block": "Следующий блок",
|
||||
"explorer_no_results": "Нет подходящих кэшированных блоков",
|
||||
"explorer_not_connected": "Нет подключения к демону — невозможно найти хеш блока или транзакции",
|
||||
"explorer_prev_block": "Предыдущий блок",
|
||||
"explorer_recent_blocks": "Последние блоки",
|
||||
"explorer_search": "Поиск",
|
||||
"explorer_section": "ОБОЗРЕВАТЕЛЬ",
|
||||
"explorer_tx_outputs": "Выходы",
|
||||
"explorer_tx_size": "Размер",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "Экспорт",
|
||||
"export_csv": "Экспорт в CSV",
|
||||
"export_keys_btn": "Экспорт ключей",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "Сумма",
|
||||
"send_amount_details": "ДЕТАЛИ СУММЫ",
|
||||
"send_amount_upper": "СУММА",
|
||||
"send_cannot_send_now": "Сейчас отправить нельзя — проверьте соединение, синхронизацию и доступный баланс.",
|
||||
"send_clear_fields": "Очистить все поля формы?",
|
||||
"send_contacts_button": "Выбрать из контактов",
|
||||
"send_copy_error": "Копировать ошибку",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "Операция выполняется...",
|
||||
"shield_max_utxos": "Макс. UTXO за операцию",
|
||||
"shield_merge_done": "Экранирование/объединение завершено!",
|
||||
"shield_no_zaddr_hint": "Пока нет защищённого (z-)адреса — сначала создайте его на вкладке «Получить».",
|
||||
"shield_op_failed": "Ошибка операции: ",
|
||||
"shield_op_submitted": "Операция отправлена: ",
|
||||
"shield_operation_id": "ID операции: %s",
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
"amount_label": "金额:",
|
||||
"appearance": "外观",
|
||||
"auto_shield": "自动屏蔽挖矿",
|
||||
"av_intro": "挖矿软件经常被标记为潜在有害程序。请按照以下步骤启用矿池挖矿:",
|
||||
"av_open_security": "打开 Windows 安全中心",
|
||||
"av_step1": "第 1 步:添加排除项",
|
||||
"av_step1_b1": "打开 Windows 安全中心 > 病毒和威胁防护",
|
||||
"av_step1_b2": "单击 管理设置 > 排除项 > 添加或删除",
|
||||
"av_step1_b3": "添加文件夹:%APPDATA%\\ObsidianDragon\\",
|
||||
"av_step2": "第 2 步:从隔离区还原(如果需要)",
|
||||
"av_step2_b1": "Windows 安全中心 > 保护历史记录",
|
||||
"av_step2_b2": "找到 xmrig.exe 并单击“还原”",
|
||||
"av_step3": "第 3 步:重启钱包并重试",
|
||||
"av_title": "Windows Defender 已阻止矿工程序",
|
||||
"available": "可用",
|
||||
"backup_backing_up": "正在备份...",
|
||||
"backup_create": "创建备份",
|
||||
@@ -386,13 +397,16 @@
|
||||
"explorer_mempool": "内存池",
|
||||
"explorer_mempool_size": "大小",
|
||||
"explorer_mempool_txs": "交易",
|
||||
"explorer_next_block": "下一个区块",
|
||||
"explorer_no_results": "没有匹配的缓存区块",
|
||||
"explorer_not_connected": "未连接到守护进程——无法查询区块或交易哈希",
|
||||
"explorer_prev_block": "上一个区块",
|
||||
"explorer_recent_blocks": "最近区块",
|
||||
"explorer_search": "搜索",
|
||||
"explorer_section": "浏览器",
|
||||
"explorer_tx_outputs": "输出",
|
||||
"explorer_tx_size": "大小",
|
||||
"explorer_txid": "TxID:",
|
||||
"export": "导出",
|
||||
"export_csv": "导出 CSV",
|
||||
"export_keys_btn": "导出密钥",
|
||||
@@ -1070,6 +1084,7 @@
|
||||
"send_amount": "金额",
|
||||
"send_amount_details": "金额详情",
|
||||
"send_amount_upper": "金额",
|
||||
"send_cannot_send_now": "现在无法发送 — 请检查连接、同步和可用余额。",
|
||||
"send_clear_fields": "清除所有表单字段?",
|
||||
"send_contacts_button": "从联系人中选择",
|
||||
"send_copy_error": "复制错误",
|
||||
@@ -1208,6 +1223,7 @@
|
||||
"shield_in_progress": "操作进行中...",
|
||||
"shield_max_utxos": "每次操作最大 UTXO 数",
|
||||
"shield_merge_done": "屏蔽/合并完成!",
|
||||
"shield_no_zaddr_hint": "还没有屏蔽(z)地址 — 请先在“接收”标签页创建一个。",
|
||||
"shield_op_failed": "操作失败:",
|
||||
"shield_op_submitted": "操作已提交:",
|
||||
"shield_operation_id": "操作 ID:%s",
|
||||
|
||||
60
src/app.cpp
60
src/app.cpp
@@ -3368,7 +3368,11 @@ void App::maybeOfferDaemonUpdate()
|
||||
|
||||
void App::renderDaemonUpdatePrompt()
|
||||
{
|
||||
if (!ui::material::BeginOverlayDialog(TR("daemon_update_title"), &show_daemon_update_prompt_, 520.0f, 0.94f))
|
||||
ui::material::OverlayDialogSpec ov;
|
||||
ov.title = TR("daemon_update_title"); ov.p_open = &show_daemon_update_prompt_;
|
||||
ov.style = ui::material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 520.0f; ov.idSuffix = "daemonupdprompt";
|
||||
if (!ui::material::BeginOverlayDialog(ov))
|
||||
return;
|
||||
const float dp = ui::Layout::dpiScale();
|
||||
ImGui::TextWrapped("%s", TR("daemon_update_body"));
|
||||
@@ -3377,13 +3381,12 @@ void App::renderDaemonUpdatePrompt()
|
||||
ImGui::TextWrapped("%s", TR("daemon_update_safe"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("daemon_update_now"), ImVec2(170 * dp, 0))) {
|
||||
if (ui::material::TactileButton(TR("daemon_update_now"), ImVec2(170 * dp, 0))) {
|
||||
show_daemon_update_prompt_ = false;
|
||||
reinstallBundledDaemon(); // stops the node (managed or external), overwrites, restarts
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("daemon_update_keep"), ImVec2(150 * dp, 0))) {
|
||||
if (ui::material::TactileButton(TR("daemon_update_keep"), ImVec2(150 * dp, 0))) {
|
||||
show_daemon_update_prompt_ = false;
|
||||
}
|
||||
ui::material::EndOverlayDialog();
|
||||
@@ -3817,42 +3820,43 @@ void App::renderAntivirusHelpDialog()
|
||||
#ifdef _WIN32
|
||||
if (!pending_antivirus_dialog_) return;
|
||||
|
||||
if (!ui::material::BeginOverlayDialog("Windows Defender Blocked Miner", &pending_antivirus_dialog_, 560.0f, 0.94f)) {
|
||||
ui::material::OverlayDialogSpec ov;
|
||||
ov.title = TR("av_title"); ov.p_open = &pending_antivirus_dialog_;
|
||||
ov.style = ui::material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 560.0f; ov.idSuffix = "antivirus";
|
||||
if (!ui::material::BeginOverlayDialog(ov)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"Mining software is often flagged as potentially unwanted. "
|
||||
"Follow these steps to enable pool mining:");
|
||||
|
||||
const float dp = ui::Layout::dpiScale();
|
||||
|
||||
ImGui::TextWrapped("%s", TR("av_intro"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, "Step 1: Add Exclusion");
|
||||
ImGui::BulletText("Open Windows Security > Virus & threat protection");
|
||||
ImGui::BulletText("Click Manage settings > Exclusions > Add or remove");
|
||||
ImGui::BulletText("Add folder: %%APPDATA%%\\ObsidianDragon\\");
|
||||
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, TR("av_step1"));
|
||||
ImGui::BulletText("%s", TR("av_step1_b1"));
|
||||
ImGui::BulletText("%s", TR("av_step1_b2"));
|
||||
ImGui::BulletText("%s", TR("av_step1_b3"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, "Step 2: Restore from Quarantine (if needed)");
|
||||
ImGui::BulletText("Windows Security > Protection history");
|
||||
ImGui::BulletText("Find xmrig.exe and click Restore");
|
||||
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, TR("av_step2"));
|
||||
ImGui::BulletText("%s", TR("av_step2_b1"));
|
||||
ImGui::BulletText("%s", TR("av_step2_b2"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, "Step 3: Restart wallet and try again");
|
||||
|
||||
ui::material::Type().text(ui::material::TypeStyle::Subtitle2, TR("av_step3"));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd()));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||
|
||||
float btnW = 160.0f;
|
||||
if (ui::material::StyledButton("Open Windows Security", ImVec2(btnW, 0))) {
|
||||
|
||||
float btnW = 160.0f * dp;
|
||||
if (ui::material::TactileButton(TR("av_open_security"), ImVec2(btnW, 0))) {
|
||||
// Open Windows Security app to the exclusions page
|
||||
ShellExecuteA(NULL, "open", "windowsdefender://threat", NULL, NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton("Close", ImVec2(100.0f, 0))) {
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(100.0f * dp, 0))) {
|
||||
pending_antivirus_dialog_ = false;
|
||||
}
|
||||
|
||||
|
||||
ui::material::EndOverlayDialog();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
#include "ui/windows/bootstrap_download_dialog.h"
|
||||
#include "ui/windows/daemon_download_dialog.h"
|
||||
#include "ui/windows/xmrig_download_dialog.h"
|
||||
#include "ui/windows/qr_popup_dialog.h"
|
||||
#include "ui/windows/request_payment_dialog.h"
|
||||
#include "ui/windows/validate_address_dialog.h"
|
||||
#include "ui/windows/address_label_dialog.h"
|
||||
#include "ui/windows/shield_dialog.h"
|
||||
#include "ui/windows/address_transfer_dialog.h"
|
||||
#include "ui/windows/key_export_dialog.h"
|
||||
#include "ui/pages/settings_page.h"
|
||||
#include "util/platform.h"
|
||||
#include "wallet/wallet_capabilities.h"
|
||||
@@ -325,6 +332,47 @@ void App::buildSweepCatalog()
|
||||
a.show_pin_remove_ = false; a.pin_status_.clear();
|
||||
memset(a.pin_old_buf_, 0, sizeof(a.pin_old_buf_));
|
||||
});
|
||||
// Wave-1 standalone dialogs (rendered globally from App::render, so any page works).
|
||||
add("modal-qr-popup", ui::NavPage::Receive,
|
||||
[](App&) { ui::QRPopupDialog::show(kDemoZAddr, "Savings"); },
|
||||
[](App&) { ui::QRPopupDialog::close(); });
|
||||
add("modal-request-payment", ui::NavPage::Receive,
|
||||
[](App&) { ui::RequestPaymentDialog::show(kDemoZAddr); },
|
||||
[](App&) { ui::RequestPaymentDialog::hide(); });
|
||||
add("modal-validate-address", ui::NavPage::Receive,
|
||||
[](App&) { ui::ValidateAddressDialog::show(); },
|
||||
[](App&) { ui::ValidateAddressDialog::hide(); });
|
||||
add("modal-address-label", ui::NavPage::Overview,
|
||||
[](App& a) { ui::AddressLabelDialog::show(&a, kDemoZAddr, true); },
|
||||
[](App&) { ui::AddressLabelDialog::hide(); });
|
||||
// (No modal-daemon-prompt surface: renderDaemonUpdatePrompt is gated behind !capture_mode_,
|
||||
// so it can't render during a sweep. Its migration is verified by build + the shared overlay pattern.)
|
||||
add("modal-antivirus", ui::NavPage::Mining,
|
||||
[](App& a) { a.pending_antivirus_dialog_ = true; },
|
||||
[](App& a) { a.pending_antivirus_dialog_ = false; });
|
||||
// Wave-2 fund/secret dialogs (setup never fires the async RPC — no button is clicked).
|
||||
add("modal-shield", ui::NavPage::Send,
|
||||
[](App&) { ui::ShieldDialog::showShieldCoinbase(); },
|
||||
[](App&) { ui::ShieldDialog::hide(); });
|
||||
add("modal-merge", ui::NavPage::Send,
|
||||
[](App&) { ui::ShieldDialog::showMerge(); },
|
||||
[](App&) { ui::ShieldDialog::hide(); });
|
||||
// z->t transfer so the (converted) deshielding DialogWarningHeader renders.
|
||||
add("modal-transfer", ui::NavPage::Overview,
|
||||
[](App& a) {
|
||||
ui::AddressTransferInfo info;
|
||||
info.fromAddr = kDemoZAddr;
|
||||
info.toAddr = "t1DemoTransparentReceiveAddress00000";
|
||||
info.fromBalance = 12.5; info.toBalance = 3.0;
|
||||
info.fromIsZ = true; info.toIsZ = false;
|
||||
ui::AddressTransferDialog::show(&a, info);
|
||||
},
|
||||
[](App&) { ui::AddressTransferDialog::close(); });
|
||||
// Distinct from the settings "modal-export-key" (App::renderExportKeyDialog) — this is the
|
||||
// per-address KeyExportDialog opened from Overview address rows.
|
||||
add("modal-key-export", ui::NavPage::Overview,
|
||||
[](App&) { ui::KeyExportDialog::show(kDemoZAddr, ui::KeyExportDialog::KeyType::Private); },
|
||||
[](App&) { ui::KeyExportDialog::hide(); });
|
||||
add("modal-about", ui::NavPage::Overview,
|
||||
[](App& a) { a.show_about_ = true; }, [](App& a) { a.show_about_ = false; });
|
||||
add("modal-settings", ui::NavPage::Settings,
|
||||
|
||||
@@ -746,13 +746,13 @@ inline void DrawFullWindowBlurBackdrop(ImDrawList* dl, const ImVec2& pMin, const
|
||||
params.absoluteBlurRadius = true; // fixed modal blur — independent of the user's acrylic slider
|
||||
params.fallbackColor.w = 1.0f; // full-strength blur so the live content reads
|
||||
effects::ImGuiAcrylic::DrawAcrylicRect(dl, pMin, pMax, params, 0.0f);
|
||||
// Dim over the blur so the floating (card-less) modal reads as foreground. On DARK themes the
|
||||
// theme-tinted frost darkens the backdrop and the light content pops. On LIGHT themes that same
|
||||
// tint whitens the backdrop and leaves card-less content with no figure/ground separation — so
|
||||
// light themes get a neutral dark veil instead, which also makes the modal's fields/buttons stand
|
||||
// out from the (now slightly greyed) surround. Both a touch stronger than before (was alpha 70).
|
||||
// Veil over the blur so the floating (card-less) modal reads as foreground. On DARK themes a
|
||||
// theme-tinted frost darkens the backdrop and the light content pops. On LIGHT themes the modal's
|
||||
// own controls carry a dropshadow for figure/ground, so the backdrop stays LIGHT — a theme-tinted
|
||||
// light frost — rather than a dark veil (which made a "light" theme read dark and sank the
|
||||
// dark-on-light chrome into it).
|
||||
if (IsLightTheme())
|
||||
dl->AddRectFilled(pMin, pMax, IM_COL32(16, 18, 24, 200)); // neutral dark veil anchors on white
|
||||
dl->AddRectFilled(pMin, pMax, WithAlpha(Background(), 205)); // light frost — dropshadows give shape
|
||||
else
|
||||
dl->AddRectFilled(pMin, pMax, WithAlpha(Background(), 120)); // theme-tinted darken, stronger
|
||||
}
|
||||
|
||||
@@ -61,7 +61,12 @@ public:
|
||||
|
||||
constexpr float cardTopViewportRatio = 0.15f;
|
||||
constexpr float cardBottomViewportRatio = 0.80f;
|
||||
if (BeginOverlayDialog(TR("set_label"), &s_open, 660.0f, 0.92f, cardBottomViewportRatio)) {
|
||||
OverlayDialogSpec ov;
|
||||
ov.title = TR("set_label"); ov.p_open = &s_open;
|
||||
ov.style = OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 660.0f; ov.idSuffix = "addrlabel";
|
||||
ov.cardBottomViewportRatio = cardBottomViewportRatio;
|
||||
if (BeginOverlayDialog(ov)) {
|
||||
float dp = Layout::dpiScale();
|
||||
|
||||
// Address preview
|
||||
@@ -108,7 +113,7 @@ public:
|
||||
const bool showClearIcon = (s_selectedIcon >= 0);
|
||||
const float clearRowH = showClearIcon ? ImGui::GetTextLineHeight() : 0.0f;
|
||||
const float buttonRowH = ImGui::GetFrameHeight();
|
||||
const float separatorH = 1.0f;
|
||||
const float separatorH = 0.0f; // footer divider removed (reference design)
|
||||
const float preButtonReserve =
|
||||
(showClearIcon ? (style.ItemSpacing.y + clearRowH) : 0.0f) +
|
||||
style.ItemSpacing.y * 3.0f + separatorH;
|
||||
@@ -196,7 +201,6 @@ public:
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::GetCursorPosY() < buttonY) {
|
||||
@@ -247,6 +251,9 @@ public:
|
||||
|
||||
static bool isOpen() { return s_open; }
|
||||
|
||||
// Close the dialog (used by the UI sweep teardown).
|
||||
static void hide() { s_open = false; }
|
||||
|
||||
private:
|
||||
static inline bool s_open = false;
|
||||
static inline App* s_app = nullptr;
|
||||
|
||||
@@ -59,7 +59,11 @@ public:
|
||||
|
||||
using namespace material;
|
||||
|
||||
if (BeginOverlayDialog(TR("transfer_funds"), &s_open, 620.0f, 0.94f)) {
|
||||
OverlayDialogSpec ov;
|
||||
ov.title = TR("transfer_funds"); ov.p_open = &s_open;
|
||||
ov.style = OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 620.0f; ov.idSuffix = "addrtransfer";
|
||||
if (BeginOverlayDialog(ov)) {
|
||||
float dp = Layout::dpiScale();
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
|
||||
@@ -72,9 +76,7 @@ public:
|
||||
|
||||
// Shielding / deshielding warning
|
||||
if (s_info.fromIsZ && !s_info.toIsZ) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning()));
|
||||
ImGui::TextWrapped("%s", TR("deshielding_warning"));
|
||||
ImGui::PopStyleColor();
|
||||
DialogWarningHeader(TR("deshielding_warning"));
|
||||
ImGui::Spacing();
|
||||
} else if (!s_info.fromIsZ && s_info.toIsZ) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Success()));
|
||||
@@ -188,7 +190,7 @@ public:
|
||||
buttonFont->CalcTextSizeA(buttonFontSize, 1000.0f, 0.0f, sendingLabel).x);
|
||||
float confirmW = std::max(confirmMinW, confirmTextW + buttonPadW);
|
||||
float totalW = cancelW + confirmW + Layout::spacingMd();
|
||||
float footerH = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y * 3.0f + 1.0f;
|
||||
float footerH = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y * 3.0f; // footer divider removed
|
||||
ImGuiViewport* vp = ImGui::GetMainViewport();
|
||||
float cardBottomY = vp->Pos.y + vp->Size.y * 0.85f;
|
||||
float footerTopY = cardBottomY - 24.0f * dp - footerH;
|
||||
@@ -199,7 +201,6 @@ public:
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
float rowStartX = ImGui::GetCursorPosX();
|
||||
@@ -300,7 +301,7 @@ private:
|
||||
ImGui::Spacing();
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "Operation ID: %s",
|
||||
snprintf(buf, sizeof(buf), TR("shield_operation_id"),
|
||||
s_resultMsg.size() > 40
|
||||
? (s_resultMsg.substr(0, 20) + "..." + s_resultMsg.substr(s_resultMsg.size() - 10)).c_str()
|
||||
: s_resultMsg.c_str());
|
||||
|
||||
@@ -129,8 +129,12 @@ void RenderContactsTab(App* app)
|
||||
float actionGap = actionBtn.gap > 0 ? actionBtn.gap : Layout::kDialogActionGap();
|
||||
float notesH = notesInput.height > 0 ? notesInput.height : 60.0f;
|
||||
|
||||
if (material::BeginOverlayDialog(title, open, dialogW, 0.94f,
|
||||
Layout::kDialogCompactBottomRatio(), id)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = title; ov.p_open = open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = dialogW; ov.idSuffix = id;
|
||||
ov.cardBottomViewportRatio = Layout::kDialogCompactBottomRatio();
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
// Focus the first field the frame the dialog opens so it's keyboard-ready.
|
||||
if (s_focus_edit_field) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
@@ -145,7 +149,7 @@ void RenderContactsTab(App* app)
|
||||
s_edit_address, sizeof(s_edit_address), formW);
|
||||
if (!isEdit) {
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton(TR("paste"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("paste"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
|
||||
const char* clipboard = ImGui::GetClipboardText();
|
||||
if (clipboard) copyEditField(s_edit_address, sizeof(s_edit_address), clipboard);
|
||||
}
|
||||
@@ -167,7 +171,7 @@ void RenderContactsTab(App* app)
|
||||
if (!canSubmit) ImGui::BeginDisabled();
|
||||
|
||||
const char* primaryLabel = isEdit ? TR("save") : TR("add");
|
||||
if (material::StyledButton(primaryLabel, ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(primaryLabel, ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
|
||||
// Trim the label/address (a pasted address often carries a trailing newline); keep notes as-is.
|
||||
auto trimAB = [](std::string s) {
|
||||
while (!s.empty() && (s.front()==' '||s.front()=='\t'||s.front()=='\n'||s.front()=='\r')) s.erase(s.begin());
|
||||
@@ -198,7 +202,7 @@ void RenderContactsTab(App* app)
|
||||
if (!canSubmit) ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine(0, actionGap);
|
||||
if (material::StyledButton(TR("cancel"), ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("cancel"), ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
|
||||
*open = false;
|
||||
}
|
||||
|
||||
@@ -212,7 +216,7 @@ void RenderContactsTab(App* app)
|
||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
|
||||
|
||||
// Toolbar
|
||||
if (material::StyledButton(TR("address_book_add_new"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("address_book_add_new"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
|
||||
s_show_add_dialog = true;
|
||||
s_focus_edit_field = true;
|
||||
clearEditFields();
|
||||
@@ -222,7 +226,7 @@ void RenderContactsTab(App* app)
|
||||
|
||||
if (!has_selection) ImGui::BeginDisabled();
|
||||
|
||||
if (material::StyledButton(TR("edit"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
if (material::TactileButton(TR("edit"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
openEdit();
|
||||
|
||||
ImGui::SameLine();
|
||||
@@ -230,12 +234,12 @@ void RenderContactsTab(App* app)
|
||||
// Delete — relabel to a visible confirm prompt while armed (not just a toast).
|
||||
bool armed = has_selection && s_confirm_delete_idx == s_selected_index;
|
||||
const char* delLabel = armed ? TR("address_book_confirm_delete") : TR("delete");
|
||||
if (material::StyledButton(delLabel, ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
if (material::TactileButton(delLabel, ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
doDelete();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (material::StyledButton(TR("copy_address"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
if (material::TactileButton(TR("copy_address"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
|
||||
doCopy();
|
||||
|
||||
if (!has_selection) ImGui::EndDisabled();
|
||||
|
||||
@@ -483,7 +483,7 @@ static void renderSearchBar(App* app, float availWidth) {
|
||||
|
||||
// Search button — same height as input
|
||||
ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, cursorY));
|
||||
bool clicked = material::StyledButton(TR("explorer_search"),
|
||||
bool clicked = material::TactileButton(TR("explorer_search"),
|
||||
ImVec2(btnW, barH));
|
||||
|
||||
std::string query(s_search_buf);
|
||||
@@ -1012,7 +1012,11 @@ static void renderBlockDetailModal(App* app) {
|
||||
|
||||
if (s_detail_loading && !s_show_detail_modal) {
|
||||
// Show a loading modal while fetching
|
||||
if (BeginOverlayDialog(TR("loading"), &s_detail_loading, 300.0f, 0.85f)) {
|
||||
OverlayDialogSpec lov;
|
||||
lov.title = TR("loading"); lov.p_open = &s_detail_loading;
|
||||
lov.style = OverlayStyle::BlurFloat;
|
||||
lov.cardWidth = 300.0f; lov.idSuffix = "explorerloading";
|
||||
if (BeginOverlayDialog(lov)) {
|
||||
ImGui::Spacing();
|
||||
char loadBuf[64];
|
||||
snprintf(loadBuf, sizeof(loadBuf), "%s%s", TR("loading"), material::LoadingDots());
|
||||
@@ -1055,7 +1059,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
if (app->rpc() && app->rpc()->isConnected())
|
||||
fetchBlockDetail(app, s_detail_height - 1);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("Previous block");
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("explorer_prev_block"));
|
||||
ImGui::PopID();
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
@@ -1069,7 +1073,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
if (app->rpc() && app->rpc()->isConnected())
|
||||
fetchBlockDetail(app, s_detail_height + 1);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("Next block");
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("explorer_next_block"));
|
||||
ImGui::PopID();
|
||||
ImGui::PopFont();
|
||||
}
|
||||
@@ -1077,7 +1081,6 @@ static void renderBlockDetailModal(App* app) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Show loading state inside the modal when navigating between blocks
|
||||
@@ -1253,7 +1256,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
|
||||
// Full TxID with copy
|
||||
ImGui::PushFont(capFont);
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "TxID:");
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", TR("explorer_txid"));
|
||||
ImGui::PopFont();
|
||||
ImGui::PushFont(body2);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(linkCol));
|
||||
@@ -1282,7 +1285,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
for (const auto& vout : s_tx_detail["vout"]) {
|
||||
double val = vout.value("value", 0.0);
|
||||
int n = vout.value("n", 0);
|
||||
std::string addr = "shielded";
|
||||
std::string addr = TR("shielded");
|
||||
if (vout.contains("scriptPubKey") && vout["scriptPubKey"].contains("addresses") &&
|
||||
vout["scriptPubKey"]["addresses"].is_array() && !vout["scriptPubKey"]["addresses"].empty()) {
|
||||
addr = vout["scriptPubKey"]["addresses"][0].get<std::string>();
|
||||
|
||||
@@ -58,6 +58,16 @@ bool KeyExportDialog::isOpen()
|
||||
return s_open;
|
||||
}
|
||||
|
||||
void KeyExportDialog::hide()
|
||||
{
|
||||
s_open = false;
|
||||
s_fetching = false;
|
||||
s_key.clear();
|
||||
s_show_key = false;
|
||||
s_error.clear();
|
||||
releaseQr();
|
||||
}
|
||||
|
||||
void KeyExportDialog::render(App* app)
|
||||
{
|
||||
if (!s_open) return;
|
||||
@@ -66,7 +76,6 @@ void KeyExportDialog::render(App* app)
|
||||
TR("export_private_key") : TR("export_viewing_key");
|
||||
|
||||
auto& S = schema::UI();
|
||||
auto warningBox = S.drawElement("dialogs.key-export", "warning-box");
|
||||
auto addrInput = S.input("dialogs.key-export", "address-input");
|
||||
auto revealBtn = S.button("dialogs.key-export", "reveal-button");
|
||||
auto keyDisplay = S.drawElement("dialogs.key-export", "key-display");
|
||||
@@ -79,54 +88,14 @@ void KeyExportDialog::render(App* app)
|
||||
// Modal scales to 85% of the window width. BeginOverlayDialog multiplies the width by
|
||||
// Layout::dpiScale(); divide it out here so the final card is exactly 85% at any font scale.
|
||||
const float cardW = (0.85f * ImGui::GetMainViewport()->Size.x) / Layout::dpiScale();
|
||||
if (material::BeginOverlayDialog(title, &s_open, cardW, 0.94f)) {
|
||||
ImGui::Spacing();
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = title; ov.p_open = &s_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = cardW; ov.idSuffix = "keyexport";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
material::DialogWarningHeader((s_key_type == KeyType::Private)
|
||||
? TR("key_export_private_warning") : TR("key_export_viewing_warning"));
|
||||
|
||||
// Warning section with colored background
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.6f, 0.2f, 0.2f, 0.3f));
|
||||
// Auto-size the warning box to its (wrapping, font-scaled) text so it never clips it.
|
||||
(void)warningBox;
|
||||
ImGui::BeginChild("WarningBox", ImVec2(-1, 0),
|
||||
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders);
|
||||
|
||||
// Heading — warning icon (drawn with the icon font, not the text font, so it isn't tofu) +
|
||||
// larger h6 text, centered, for emphasis.
|
||||
{
|
||||
ImFont* iconF = material::Type().iconMed();
|
||||
ImFont* textF = material::Type().h6();
|
||||
const char* icon = ICON_MD_WARNING;
|
||||
const char* txt = TR("warning_upper");
|
||||
ImGui::PushFont(iconF); const float iw = ImGui::CalcTextSize(icon).x; ImGui::PopFont();
|
||||
ImGui::PushFont(textF); const float tw = ImGui::CalcTextSize(txt).x; ImGui::PopFont();
|
||||
const float sp = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float wa = ImGui::GetContentRegionAvail().x;
|
||||
const float total = iw + sp + tw;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (wa > total ? (wa - total) * 0.5f : 0.0f));
|
||||
const ImVec4 red(1.0f, 0.42f, 0.42f, 1.0f);
|
||||
ImGui::PushFont(iconF); ImGui::TextColored(red, "%s", icon); ImGui::PopFont();
|
||||
ImGui::SameLine(0, sp);
|
||||
ImGui::PushFont(textF); ImGui::TextColored(red, "%s", txt); ImGui::PopFont();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
|
||||
// Body — centered wrapped text.
|
||||
{
|
||||
const char* body = (s_key_type == KeyType::Private)
|
||||
? TR("key_export_private_warning") : TR("key_export_viewing_warning");
|
||||
ImDrawList* wdl = ImGui::GetWindowDrawList();
|
||||
ImFont* wf = ImGui::GetFont();
|
||||
const float wfs = ImGui::GetFontSize();
|
||||
const float wa = ImGui::GetContentRegionAvail().x;
|
||||
const ImVec2 wp = ImGui::GetCursorScreenPos();
|
||||
const float bh = widgets::DrawCenteredWrapped(wdl, wf, wfs, wp, wa, material::OnSurface(), body);
|
||||
ImGui::Dummy(ImVec2(wa, bh));
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Address — click-to-copy field (chunked, width-clamped, centered).
|
||||
@@ -145,7 +114,7 @@ void KeyExportDialog::render(App* app)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), TR("error_format"), s_error.c_str());
|
||||
// Not a dead-end: clearing the error falls back through to the Reveal button next frame.
|
||||
ImGui::Spacing();
|
||||
if (material::StyledButton(TR("retry"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
||||
if (material::TactileButton(TR("retry"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
||||
s_error.clear();
|
||||
}
|
||||
} else if (s_key.empty() && s_address.length() < 26) {
|
||||
@@ -154,7 +123,7 @@ void KeyExportDialog::render(App* app)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", TR("invalid_address"));
|
||||
} else if (s_key.empty()) {
|
||||
// Show button to fetch key
|
||||
if (material::StyledButton(TR("key_export_reveal"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
||||
if (material::TactileButton(TR("key_export_reveal"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
||||
s_fetching = true;
|
||||
|
||||
// Check if z-address or t-address
|
||||
@@ -292,7 +261,7 @@ void KeyExportDialog::render(App* app)
|
||||
app->copySecretToClipboard(s_key); // auto-clearing clipboard
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (material::StyledButton(s_show_key ? TR("hide") : TR("show"), ImVec2(0, 0),
|
||||
if (material::TactileButton(s_show_key ? TR("hide") : TR("show"), ImVec2(0, 0),
|
||||
material::Type().subtitle1())) {
|
||||
s_show_key = !s_show_key;
|
||||
}
|
||||
@@ -328,8 +297,6 @@ void KeyExportDialog::render(App* app)
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Close button
|
||||
@@ -337,7 +304,7 @@ void KeyExportDialog::render(App* app)
|
||||
float avail_width = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (avail_width - button_width) / 2.0f);
|
||||
|
||||
if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::TactileButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
||||
s_open = false;
|
||||
// Clear sensitive data
|
||||
s_key.clear();
|
||||
|
||||
@@ -44,6 +44,11 @@ public:
|
||||
*/
|
||||
static bool isOpen();
|
||||
|
||||
/**
|
||||
* @brief Close the dialog and clear the revealed key (used by the UI sweep teardown)
|
||||
*/
|
||||
static void hide();
|
||||
|
||||
private:
|
||||
static bool s_open;
|
||||
static bool s_fetching;
|
||||
|
||||
@@ -67,7 +67,11 @@ void QRPopupDialog::render(App* app)
|
||||
// Match the key-export modal: 85% of the window width (divide out the dpiScale that
|
||||
// BeginOverlayDialog re-applies, so the final card is exactly 85% at any font scale).
|
||||
const float cardW = (0.85f * ImGui::GetMainViewport()->Size.x) / Layout::dpiScale();
|
||||
if (material::BeginOverlayDialog(TR("qr_title"), &s_open, cardW, 0.94f)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("qr_title"); ov.p_open = &s_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = cardW; ov.idSuffix = "qrpopup";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
|
||||
// Label if present
|
||||
if (!s_label.empty()) {
|
||||
@@ -120,11 +124,11 @@ void QRPopupDialog::render(App* app)
|
||||
const float total_width = w_copy + w_close + ImGui::GetStyle().ItemSpacing.x;
|
||||
ImGui::SetCursorPosX((window_width - total_width) / 2.0f);
|
||||
|
||||
if (material::StyledButton(TR("copy_address"), ImVec2(w_copy, 0), btnFont)) {
|
||||
if (material::TactileButton(TR("copy_address"), ImVec2(w_copy, 0), btnFont)) {
|
||||
ImGui::SetClipboardText(s_address.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton(TR("close"), ImVec2(w_close, 0), btnFont)) {
|
||||
if (material::TactileButton(TR("close"), ImVec2(w_close, 0), btnFont)) {
|
||||
close();
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
|
||||
@@ -59,6 +59,15 @@ void RequestPaymentDialog::show(const std::string& address)
|
||||
}
|
||||
}
|
||||
|
||||
void RequestPaymentDialog::hide()
|
||||
{
|
||||
s_open = false;
|
||||
if (s_qr_texture != 0) {
|
||||
FreeQRTexture(s_qr_texture);
|
||||
s_qr_texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestPaymentDialog::render(App* app)
|
||||
{
|
||||
if (!s_open) return;
|
||||
@@ -76,13 +85,15 @@ void RequestPaymentDialog::render(App* app)
|
||||
auto qr = S.drawElement("dialogs.request-payment", "qr-code");
|
||||
auto actionBtn = S.button("dialogs.request-payment", "action-button");
|
||||
|
||||
if (material::BeginOverlayDialog(TR("request_title"), &s_open, win.width, 0.94f)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("request_title"); ov.p_open = &s_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = win.width; ov.idSuffix = "requestpay";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
const auto& state = app->getWalletState();
|
||||
|
||||
|
||||
ImGui::TextWrapped("%s", TR("request_description"));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Address selection
|
||||
@@ -196,14 +207,14 @@ void RequestPaymentDialog::render(App* app)
|
||||
ImGui::Spacing();
|
||||
|
||||
// Copy button
|
||||
if (material::StyledButton(TR("request_copy_uri"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("request_copy_uri"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
ImGui::SetClipboardText(s_payment_uri.c_str());
|
||||
Notifications::instance().success(TR("request_uri_copied"));
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (material::StyledButton(TR("copy_address"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("copy_address"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
ImGui::SetClipboardText(s_address);
|
||||
Notifications::instance().success(TR("address_copied"));
|
||||
}
|
||||
@@ -212,7 +223,7 @@ void RequestPaymentDialog::render(App* app)
|
||||
ImGui::Spacing();
|
||||
|
||||
// Close button
|
||||
if (material::StyledButton(TR("close"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
if (material::TactileButton(TR("close"), ImVec2(actionBtn.width, 0), S.resolveFont(actionBtn.font))) {
|
||||
s_open = false;
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
|
||||
@@ -26,6 +26,11 @@ public:
|
||||
* @brief Render the dialog (call each frame)
|
||||
*/
|
||||
static void render(App* app);
|
||||
|
||||
/**
|
||||
* @brief Close the dialog + free its QR texture (used by the UI sweep teardown)
|
||||
*/
|
||||
static void hide();
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -696,7 +696,11 @@ void RenderSendConfirmPopup(App* app) {
|
||||
float popupAvailW = ImGui::GetMainViewport()->Size.x * S.drawElement("tabs.send", "confirm-popup-width-ratio").size;
|
||||
float popupW = std::min(schema::UI().drawElement("tabs.send", "confirm-popup-max-width").size, popupAvailW);
|
||||
float popVs = Layout::vScale();
|
||||
if (material::BeginOverlayDialog(TR("confirm_send"), nullptr, popupW, 0.94f)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("confirm_send"); ov.p_open = nullptr;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = popupW; ov.idSuffix = "confirmsend";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape) && !s_sending) {
|
||||
s_show_confirm = false;
|
||||
@@ -826,7 +830,7 @@ void RenderSendConfirmPopup(App* app) {
|
||||
double liveAvail = GetAvailableBalance(app);
|
||||
if (!app->isConnected() || liveState.sync.syncing || s_amount <= 0 ||
|
||||
(s_amount + s_fee) > liveAvail) {
|
||||
s_tx_status = "Cannot send now — check connection, sync, and available balance.";
|
||||
s_tx_status = TR("send_cannot_send_now");
|
||||
s_status_success = false;
|
||||
s_status_timestamp = ImGui::GetTime();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,14 @@ void ShieldDialog::showMerge()
|
||||
show(Mode::MergeToAddress);
|
||||
}
|
||||
|
||||
void ShieldDialog::hide()
|
||||
{
|
||||
s_open = false;
|
||||
s_operation_pending = false;
|
||||
s_status_message.clear();
|
||||
s_operation_id.clear();
|
||||
}
|
||||
|
||||
void ShieldDialog::render(App* app)
|
||||
{
|
||||
if (!s_open) return;
|
||||
@@ -79,18 +87,20 @@ void ShieldDialog::render(App* app)
|
||||
? TR("shield_title")
|
||||
: TR("merge_title");
|
||||
|
||||
if (material::BeginOverlayDialog(title, &s_open, win.width, 0.94f)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = title; ov.p_open = &s_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = win.width; ov.idSuffix = "shielddialog";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
const auto& state = app->getWalletState();
|
||||
|
||||
|
||||
// Description
|
||||
if (s_mode == Mode::ShieldCoinbase) {
|
||||
ImGui::TextWrapped("%s", TR("shield_description"));
|
||||
} else {
|
||||
ImGui::TextWrapped("%s", TR("merge_description"));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// From address (for shield coinbase)
|
||||
@@ -131,7 +141,7 @@ void ShieldDialog::render(App* app)
|
||||
}
|
||||
if (state.z_addresses.empty()) {
|
||||
material::Type().textColored(material::TypeStyle::Caption, material::Warning(),
|
||||
"No shielded (z) address yet — create one on the Receive tab first.");
|
||||
TR("shield_no_zaddr_hint"));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
@@ -156,8 +166,6 @@ void ShieldDialog::render(App* app)
|
||||
if (s_utxo_limit < 1) s_utxo_limit = 1;
|
||||
if (s_utxo_limit > 100) s_utxo_limit = 100;
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Status message
|
||||
@@ -179,7 +187,7 @@ void ShieldDialog::render(App* app)
|
||||
if (!can_submit) ImGui::BeginDisabled();
|
||||
|
||||
const char* btn_label = (s_mode == Mode::ShieldCoinbase) ? TR("shield_funds") : TR("merge_funds");
|
||||
if (material::StyledButton(btn_label, ImVec2(shieldBtn.width, 0), S.resolveFont(shieldBtn.font))) {
|
||||
if (material::TactileButton(btn_label, ImVec2(shieldBtn.width, 0), S.resolveFont(shieldBtn.font))) {
|
||||
s_operation_pending = true;
|
||||
s_status_message = TR("shield_submitting");
|
||||
|
||||
@@ -259,7 +267,7 @@ void ShieldDialog::render(App* app)
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (material::StyledButton(TR("cancel"), ImVec2(cancelBtn.width, 0), S.resolveFont(cancelBtn.font))) {
|
||||
if (material::TactileButton(TR("cancel"), ImVec2(cancelBtn.width, 0), S.resolveFont(cancelBtn.font))) {
|
||||
s_open = false;
|
||||
}
|
||||
|
||||
@@ -271,7 +279,7 @@ void ShieldDialog::render(App* app)
|
||||
|
||||
ImGui::Text(TR("shield_operation_id"), s_operation_id.c_str());
|
||||
|
||||
if (material::StyledButton(TR("shield_check_status"), ImVec2(0,0), S.resolveFont(shieldBtn.font))) {
|
||||
if (material::TactileButton(TR("shield_check_status"), ImVec2(0,0), S.resolveFont(shieldBtn.font))) {
|
||||
std::string opid = s_operation_id;
|
||||
if (app->worker()) {
|
||||
app->worker()->post([rpc = app->rpc(), opid]() -> rpc::RPCWorker::MainCb {
|
||||
|
||||
@@ -41,6 +41,11 @@ public:
|
||||
* @brief Render the dialog (call each frame)
|
||||
*/
|
||||
static void render(App* app);
|
||||
|
||||
/**
|
||||
* @brief Close the dialog (used by the UI sweep teardown)
|
||||
*/
|
||||
static void hide();
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -42,6 +42,11 @@ bool ValidateAddressDialog::isOpen()
|
||||
return s_open;
|
||||
}
|
||||
|
||||
void ValidateAddressDialog::hide()
|
||||
{
|
||||
s_open = false;
|
||||
}
|
||||
|
||||
void ValidateAddressDialog::render(App* app)
|
||||
{
|
||||
if (!s_open) return;
|
||||
@@ -53,11 +58,13 @@ void ValidateAddressDialog::render(App* app)
|
||||
auto lbl = S.label("dialogs.validate-address", "label");
|
||||
auto closeBtn = S.button("dialogs.validate-address", "close-button");
|
||||
|
||||
if (material::BeginOverlayDialog(TR("validate_title"), &s_open, win.width, 0.94f)) {
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("validate_title"); ov.p_open = &s_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = win.width; ov.idSuffix = "validateaddr";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
ImGui::TextWrapped("%s", TR("validate_description"));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Address input
|
||||
@@ -74,7 +81,7 @@ void ValidateAddressDialog::render(App* app)
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (material::StyledButton(TR("validate_btn"), ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) {
|
||||
if (material::TactileButton(TR("validate_btn"), ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) {
|
||||
s_validating = true;
|
||||
s_validated = false;
|
||||
s_error_message.clear();
|
||||
@@ -150,7 +157,7 @@ void ValidateAddressDialog::render(App* app)
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (material::StyledButton(TR("paste"), ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) {
|
||||
if (material::TactileButton(TR("paste"), ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) {
|
||||
const char* clipboard = ImGui::GetClipboardText();
|
||||
if (clipboard) {
|
||||
strncpy(s_address_input, clipboard, sizeof(s_address_input) - 1);
|
||||
@@ -164,8 +171,6 @@ void ValidateAddressDialog::render(App* app)
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "%s", TR("validating"));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Results
|
||||
@@ -174,7 +179,7 @@ void ValidateAddressDialog::render(App* app)
|
||||
ImGui::Spacing();
|
||||
|
||||
if (!s_error_message.empty()) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Error: %s", s_error_message.c_str());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), TR("error_format"), s_error_message.c_str());
|
||||
} else {
|
||||
// Valid/Invalid indicator
|
||||
ImGui::Text("%s", TR("validate_status"));
|
||||
@@ -210,7 +215,7 @@ void ValidateAddressDialog::render(App* app)
|
||||
// Close button at bottom
|
||||
float button_width = closeBtn.width;
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - button_width) / 2.0f);
|
||||
if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::TactileButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
||||
s_open = false;
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
|
||||
@@ -33,6 +33,11 @@ public:
|
||||
*/
|
||||
static bool isOpen();
|
||||
|
||||
/**
|
||||
* @brief Close the dialog (used by the UI sweep teardown)
|
||||
*/
|
||||
static void hide();
|
||||
|
||||
private:
|
||||
static bool s_open;
|
||||
static bool s_validated;
|
||||
|
||||
@@ -1837,6 +1837,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["send_total"] = "Total";
|
||||
strings_["send_tx_failed"] = "Transaction failed";
|
||||
strings_["send_tx_sent"] = "Transaction sent!";
|
||||
strings_["send_cannot_send_now"] = "Cannot send now — check connection, sync, and available balance.";
|
||||
strings_["send_tx_success"] = "Transaction sent successfully!";
|
||||
strings_["send_status_unconfirmed"] = "Transaction status could not be confirmed";
|
||||
strings_["send_err_needs_rescan"] = "Your wallet's shielded note data is out of date with the blockchain (this happens after a bootstrap or reindex). Run a full rescan via Settings -> Rescan Blockchain and let it finish completely, then try sending again.";
|
||||
@@ -1857,6 +1858,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["shield_max_utxos"] = "Max UTXOs per operation";
|
||||
strings_["shield_merge_done"] = "Shield/merge completed!";
|
||||
strings_["shield_select_z"] = "Select z-address...";
|
||||
strings_["shield_no_zaddr_hint"] = "No shielded (z) address yet — create one on the Receive tab first.";
|
||||
strings_["shield_started"] = "Shield operation started";
|
||||
strings_["shield_title"] = "Shield Coinbase Rewards";
|
||||
strings_["shield_to_address"] = "To Address (Shielded):";
|
||||
@@ -1937,6 +1939,21 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["explorer_block_merkle"] = "Merkle Root";
|
||||
strings_["explorer_tx_outputs"] = "Outputs";
|
||||
strings_["explorer_tx_size"] = "Size";
|
||||
strings_["explorer_txid"] = "TxID:";
|
||||
strings_["explorer_prev_block"] = "Previous block";
|
||||
strings_["explorer_next_block"] = "Next block";
|
||||
// --- Antivirus (Windows Defender) help dialog (Windows-only) ---
|
||||
strings_["av_title"] = "Windows Defender Blocked Miner";
|
||||
strings_["av_intro"] = "Mining software is often flagged as potentially unwanted. Follow these steps to enable pool mining:";
|
||||
strings_["av_step1"] = "Step 1: Add Exclusion";
|
||||
strings_["av_step1_b1"] = "Open Windows Security > Virus & threat protection";
|
||||
strings_["av_step1_b2"] = "Click Manage settings > Exclusions > Add or remove";
|
||||
strings_["av_step1_b3"] = "Add folder: %APPDATA%\\ObsidianDragon\\";
|
||||
strings_["av_step2"] = "Step 2: Restore from Quarantine (if needed)";
|
||||
strings_["av_step2_b1"] = "Windows Security > Protection history";
|
||||
strings_["av_step2_b2"] = "Find xmrig.exe and click Restore";
|
||||
strings_["av_step3"] = "Step 3: Restart wallet and try again";
|
||||
strings_["av_open_security"] = "Open Windows Security";
|
||||
strings_["explorer_invalid_query"] = "Enter a block height or 64-character hash";
|
||||
strings_["explorer_invalid_response"] = "Invalid response from daemon";
|
||||
strings_["explorer_hash_not_found"] = "No block or transaction found for this hash";
|
||||
|
||||
Reference in New Issue
Block a user