Compare commits
6
Commits
e40c6c0a7c
...
064c9d1b61
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
064c9d1b61 | ||
|
|
2ef91ea7d4 | ||
|
|
19da7d1474 | ||
|
|
faf45e60e4 | ||
|
|
2282f332a6 | ||
|
|
5fcab14e4e |
Binary file not shown.
@@ -639,6 +639,11 @@
|
||||
"download": "Herunterladen",
|
||||
"download_bootstrap": "Bootstrap herunterladen",
|
||||
"dragonx_green": "DragonX (Grün)",
|
||||
"earnings_format": "Anzeigeformat für Erträge",
|
||||
"earnings_format_coin": "Nur Münze",
|
||||
"earnings_format_coin_fiat": "Münze + Fiat",
|
||||
"earnings_format_fiat": "Nur Fiat",
|
||||
"earnings_label": "Erträge",
|
||||
"edit": "Bearbeiten",
|
||||
"empty_wallet_keys_suffix": "Schlüssel",
|
||||
"empty_wallet_open_manager": "Wallet-Verwaltung öffnen",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "Wie zeige ich Fiat-Preise an?",
|
||||
"faq_w_set_title": "Einstellungen & Erscheinungsbild",
|
||||
"favorite_address": "Als Favorit markieren",
|
||||
"favorite_opacity": "Deckkraft der Favoriten-Hervorhebung",
|
||||
"fee": "Gebühr",
|
||||
"fee_high": "Hoch",
|
||||
"fee_label": "Gebühr:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (versteckt)",
|
||||
"hide": "Ausblenden",
|
||||
"hide_address": "Adresse ausblenden",
|
||||
"hide_earnings": "Erträge ausblenden",
|
||||
"hide_qr": "QR ausblenden",
|
||||
"hide_zero_balances": "Nullsalden ausblenden",
|
||||
"history": "Verlauf",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "Gesch. Block",
|
||||
"mining_est_daily": "Gesch. täglich",
|
||||
"mining_est_daily_pool_sub": "grobe Solo-Äquivalenz, vor Pool-Gebühr",
|
||||
"mining_est_daily_sub": "Ø",
|
||||
"mining_est_daily_sub_net": "Ø, netto",
|
||||
"mining_filter_all": "Alle",
|
||||
"mining_filter_tip_all": "Alle Einnahmen anzeigen",
|
||||
"mining_filter_tip_pool": "Nur Pool-Einnahmen anzeigen",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "Abgeschirmt",
|
||||
"shielding_notice": "Hinweis: Dies wird Gelder von einer transparenten (T) Adresse auf eine private (Z) Adresse schirmen.",
|
||||
"show": "Anzeigen",
|
||||
"show_earnings": "Erträge anzeigen",
|
||||
"show_hidden": "Ausgeblendete anzeigen (%d)",
|
||||
"show_qr": "QR anzeigen",
|
||||
"show_qr_code": "QR-Code anzeigen",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "Debug-Protokollierungsoptionen ausklappen",
|
||||
"tt_delete_blockchain": "Alle Blockchain-Daten löschen und neu synchronisieren. wallet.dat und Konfiguration bleiben erhalten.",
|
||||
"tt_download_bootstrap": "Blockchain-Bootstrap herunterladen, um die Synchronisierung zu beschleunigen\nVorhandene Blockdaten werden ersetzt",
|
||||
"tt_earn_interval": "Ertragszeitraum: 1H = diese Stunde, 1D = heute, 1M = dieser Monat, 1Y = dieses Jahr",
|
||||
"tt_earn_mode_inflows": "Ertragsmodus: erhaltene Beträge (einschließlich Mining)",
|
||||
"tt_earn_mode_net": "Ertragsmodus: netto (erhalten minus gesendet)",
|
||||
"tt_earn_show_all": "Ertragswert für jede Adresse anzeigen",
|
||||
"tt_earnings_format": "Wie die Ertragswerte pro Adresse und insgesamt angezeigt werden",
|
||||
"tt_encrypt": "wallet.dat mit einer Passphrase verschlüsseln",
|
||||
"tt_export_all": "Alle privaten Schlüssel in eine Datei exportieren",
|
||||
"tt_export_csv": "Transaktionsverlauf als CSV-Tabelle exportieren",
|
||||
"tt_export_key": "Den privaten Schlüssel der ausgewählten Adresse exportieren",
|
||||
"tt_favorite_opacity": "Deckkraft des Favoriten-Sterns und seiner goldenen Umrandung in der Adressliste (mindestens 15 %)",
|
||||
"tt_fetch_prices": "DRGX-Marktpreise von der CoinGecko-API abrufen",
|
||||
"tt_font_scale": "Alle Texte und UI skalieren (1.0x = Standard, bis 1.5x).",
|
||||
"tt_idle_delay": "Wie lange vor dem Start des Minings gewartet werden soll",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "Alle aufwendigen visuellen Effekte deaktivieren\\nHotkey: Ctrl+Shift+Down",
|
||||
"tt_merge": "Mehrere UTXOs einer Adresse zusammenführen",
|
||||
"tt_mine_idle": "Mining automatisch starten, wenn das\\nSystem inaktiv ist (keine Tastatur-/Mauseingabe)",
|
||||
"tt_mining_est_daily": "Ein langfristiger statistischer Durchschnitt für Ihren aktuellen Hashrate-Anteil am Netzwerk. Echte Mining-Belohnungen kommen zufällig (Poisson) – oft lange nichts, dann ein Batzen – das ist also KEIN gleichmäßiges Tageseinkommen.",
|
||||
"tt_mining_est_daily_fee_fmt": "Angezeigt nach der %.1f%% Pool-Gebühr.",
|
||||
"tt_mining_est_daily_nofee": "Vor Abzug der Pool-Gebühr angezeigt (Gebühr für diesen Pool unbekannt).",
|
||||
"tt_new_mining_address": "Erstellt eine transparente Adresse (R…) für Solo-Mining-Belohnungen. Sie kann keine Zahlungen empfangen – teilen Sie dafür eine abgeschirmte Adresse.",
|
||||
"tt_noise": "Körnungstextur-Intensität (0% = aus, 100% = maximum)",
|
||||
"tt_open_app_dir": "Den ObsidianDragon-Ordner (Einstellungen, Themes, Logs) im Dateimanager öffnen",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "Karten- und Seitenleisten-Deckkraft (100% = vollständig undurchsichtig, niedriger = durchsichtiger)",
|
||||
"tt_validate": "Prüfen, ob eine DragonX-Adresse gültig ist",
|
||||
"tt_verbose": "Detaillierte Verbindungsdiagnosen,\\nDaemon-Status und Port-Besitzer-Info\\nin der Konsolen-Registerkarte protokollieren",
|
||||
"tt_view_transactions": "Transaktionen mit dieser Adresse im Verlauf-Tab anzeigen",
|
||||
"tt_wallet_size_warn": "Geschützte Wallets wachsen mit den Witness-Daten jedes Notes — das Zusammenführen vieler Notes in eine Adresse verringert die Größe. Zuerst sichern.",
|
||||
"tt_wallets_button": "Ihre Wallet-Dateien auflisten und zwischen ihnen wechseln",
|
||||
"tt_website": "Die DragonX-Website öffnen",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "Alle anzeigen",
|
||||
"view_details": "Details anzeigen",
|
||||
"view_on_explorer": "Im Explorer anzeigen",
|
||||
"view_transactions": "Transaktionen anzeigen",
|
||||
"waiting_for_daemon": "Warte auf Daemon-Verbindung...",
|
||||
"wallet": "WALLET",
|
||||
"wallet_degraded_notify": "Ihre Wallet wurde im eingeschränkten Modus geöffnet: Vorhandene Guthaben sind sicher und verfügbar, aber das Erstellen neuer Adressen und das Schützen von Guthaben sind deaktiviert. Sichern Sie Ihre Seed-Phrase und stellen Sie sie wieder her, um die Wallet vollständig zu reparieren.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "Descargar",
|
||||
"download_bootstrap": "Descargar Bootstrap",
|
||||
"dragonx_green": "DragonX (Verde)",
|
||||
"earnings_format": "Formato de visualización de ganancias",
|
||||
"earnings_format_coin": "Solo moneda",
|
||||
"earnings_format_coin_fiat": "Moneda + fíat",
|
||||
"earnings_format_fiat": "Solo fíat",
|
||||
"earnings_label": "Ganancias",
|
||||
"edit": "Editar",
|
||||
"empty_wallet_keys_suffix": "claves",
|
||||
"empty_wallet_open_manager": "Abrir administrador de carteras",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "¿Cómo muestro precios en moneda fiat?",
|
||||
"faq_w_set_title": "Ajustes y apariencia",
|
||||
"favorite_address": "Marcar como favorito",
|
||||
"favorite_opacity": "Opacidad del resaltado de favoritos",
|
||||
"fee": "Comisión",
|
||||
"fee_high": "Alta",
|
||||
"fee_label": "Comisión:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (oculto)",
|
||||
"hide": "Ocultar",
|
||||
"hide_address": "Ocultar dirección",
|
||||
"hide_earnings": "Ocultar ganancias",
|
||||
"hide_qr": "Ocultar QR",
|
||||
"hide_zero_balances": "Ocultar saldos 0",
|
||||
"history": "Historial",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "Bloque Est.",
|
||||
"mining_est_daily": "Diario Est.",
|
||||
"mining_est_daily_pool_sub": "equivalente solo aproximado, antes de la comisión del pool",
|
||||
"mining_est_daily_sub": "prom.",
|
||||
"mining_est_daily_sub_net": "prom., neto",
|
||||
"mining_filter_all": "Todos",
|
||||
"mining_filter_tip_all": "Mostrar todas las ganancias",
|
||||
"mining_filter_tip_pool": "Mostrar solo ganancias del pool",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "Protegido",
|
||||
"shielding_notice": "Nota: Esto blindará fondos de una dirección transparente (T) a una dirección privada (Z).",
|
||||
"show": "Mostrar",
|
||||
"show_earnings": "Mostrar ganancias",
|
||||
"show_hidden": "Mostrar ocultos (%d)",
|
||||
"show_qr": "Mostrar QR",
|
||||
"show_qr_code": "Mostrar Código QR",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "Expandir opciones de registro de depuración",
|
||||
"tt_delete_blockchain": "Eliminar todos los datos de la blockchain e iniciar una nueva sincronización. Se preservan wallet.dat y la configuración.",
|
||||
"tt_download_bootstrap": "Descargar bootstrap de blockchain para acelerar la sincronización\nLos datos de bloques existentes serán reemplazados",
|
||||
"tt_earn_interval": "Periodo de ganancias: 1H = esta hora, 1D = hoy, 1M = este mes, 1Y = este año",
|
||||
"tt_earn_mode_inflows": "Modo de ganancias: importes recibidos (incluida la minería)",
|
||||
"tt_earn_mode_net": "Modo de ganancias: neto (recibido menos enviado)",
|
||||
"tt_earn_show_all": "Mostrar la cifra de ganancias para cada dirección",
|
||||
"tt_earnings_format": "Cómo se muestran las cifras de ganancias por dirección y totales",
|
||||
"tt_encrypt": "Cifrar wallet.dat con una contraseña",
|
||||
"tt_export_all": "Exportar todas las claves privadas a un archivo",
|
||||
"tt_export_csv": "Exportar historial de transacciones como hoja de cálculo CSV",
|
||||
"tt_export_key": "Exportar la clave privada de la dirección seleccionada",
|
||||
"tt_favorite_opacity": "Opacidad del botón de estrella de favoritos y su contorno dorado en la lista de direcciones (mínimo 15 %)",
|
||||
"tt_fetch_prices": "Obtener precios de mercado de DRGX desde la API de CoinGecko",
|
||||
"tt_font_scale": "Escalar todo el texto y la interfaz (1.0x = predeterminado, hasta 1.5x).",
|
||||
"tt_idle_delay": "Cuánto tiempo esperar antes de empezar a minar",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "Desactivar todos los efectos visuales pesados\\nAtajo: Ctrl+Shift+Down",
|
||||
"tt_merge": "Consolidar múltiples UTXOs en una dirección",
|
||||
"tt_mine_idle": "Iniciar minería automáticamente cuando el\\nsistema esté inactivo (sin entrada de teclado/ratón)",
|
||||
"tt_mining_est_daily": "Un promedio estadístico a largo plazo para tu proporción actual de hashrate en la red. Las recompensas reales de minería llegan al azar (Poisson): a menudo nada durante largos periodos y luego una suma; por eso NO es un ingreso diario constante.",
|
||||
"tt_mining_est_daily_fee_fmt": "Se muestra tras la comisión del pool del %.1f%%.",
|
||||
"tt_mining_est_daily_nofee": "Se muestra antes de la comisión del pool (comisión desconocida para este pool).",
|
||||
"tt_new_mining_address": "Crea una dirección transparente (R…) para recompensas de minería en solitario. No puede recibir pagos; para eso comparte una dirección blindada.",
|
||||
"tt_noise": "Intensidad de textura granulada (0% = apagado, 100% = máximo)",
|
||||
"tt_open_app_dir": "Abrir la carpeta de ObsidianDragon (configuración, temas, registros) en el explorador de archivos",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "Opacidad de tarjetas y barra lateral (100% = totalmente opaco, menor = más transparente)",
|
||||
"tt_validate": "Comprobar si una dirección DragonX es válida",
|
||||
"tt_verbose": "Registrar diagnósticos detallados de conexión,\\nestado del daemon e info de propietario de puerto\\nen la pestaña de Consola",
|
||||
"tt_view_transactions": "Mostrar las transacciones relacionadas con esta dirección en la pestaña Historial",
|
||||
"tt_wallet_size_warn": "Las carteras blindadas crecen con los datos de testigo de cada nota — fusionar muchas notas en una sola dirección lo reduce. Haz una copia de seguridad primero.",
|
||||
"tt_wallets_button": "Enumera tus archivos de cartera y cambia entre ellos",
|
||||
"tt_website": "Abrir el sitio web de DragonX",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "Ver todo",
|
||||
"view_details": "Ver Detalles",
|
||||
"view_on_explorer": "Ver en Explorador",
|
||||
"view_transactions": "Ver transacciones",
|
||||
"waiting_for_daemon": "Esperando conexión al daemon...",
|
||||
"wallet": "CARTERA",
|
||||
"wallet_degraded_notify": "Tu cartera se abrió en modo de funciones reducidas: los fondos existentes están seguros y disponibles, pero crear nuevas direcciones y blindar están desactivados. Respalda tu frase de recuperación y restáurala para reparar la cartera por completo.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "Télécharger",
|
||||
"download_bootstrap": "Télécharger Bootstrap",
|
||||
"dragonx_green": "DragonX (Vert)",
|
||||
"earnings_format": "Format d'affichage des gains",
|
||||
"earnings_format_coin": "Pièce uniquement",
|
||||
"earnings_format_coin_fiat": "Pièce + fiat",
|
||||
"earnings_format_fiat": "Fiat uniquement",
|
||||
"earnings_label": "Gains",
|
||||
"edit": "Modifier",
|
||||
"empty_wallet_keys_suffix": "clés",
|
||||
"empty_wallet_open_manager": "Ouvrir le gestionnaire de portefeuilles",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "Comment afficher les prix en monnaie fiduciaire ?",
|
||||
"faq_w_set_title": "Paramètres et apparence",
|
||||
"favorite_address": "Ajouter aux favoris",
|
||||
"favorite_opacity": "Opacité de la surbrillance des favoris",
|
||||
"fee": "Frais",
|
||||
"fee_high": "Élevés",
|
||||
"fee_label": "Frais :",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (masqué)",
|
||||
"hide": "Masquer",
|
||||
"hide_address": "Masquer l'adresse",
|
||||
"hide_earnings": "Masquer les gains",
|
||||
"hide_qr": "Masquer le QR",
|
||||
"hide_zero_balances": "Masquer les soldes à 0",
|
||||
"history": "Historique",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "Bloc est.",
|
||||
"mining_est_daily": "Est. quotidien",
|
||||
"mining_est_daily_pool_sub": "équivalent solo approximatif, avant les frais du pool",
|
||||
"mining_est_daily_sub": "moy.",
|
||||
"mining_est_daily_sub_net": "moy., net",
|
||||
"mining_filter_all": "Tout",
|
||||
"mining_filter_tip_all": "Afficher tous les gains",
|
||||
"mining_filter_tip_pool": "Afficher uniquement les gains du pool",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "Blindé",
|
||||
"shielding_notice": "Note : Cela blindera des fonds d'une adresse transparente (T) vers une adresse privée (Z).",
|
||||
"show": "Afficher",
|
||||
"show_earnings": "Afficher les gains",
|
||||
"show_hidden": "Afficher masqués (%d)",
|
||||
"show_qr": "Afficher le QR",
|
||||
"show_qr_code": "Afficher le code QR",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "Développer les options de journalisation de débogage",
|
||||
"tt_delete_blockchain": "Supprimer toutes les données de la blockchain et démarrer une nouvelle synchronisation. wallet.dat et la configuration sont préservés.",
|
||||
"tt_download_bootstrap": "Télécharger le bootstrap blockchain pour accélérer la synchronisation\nLes données de blocs existantes seront remplacées",
|
||||
"tt_earn_interval": "Période de gains : 1H = cette heure, 1D = aujourd'hui, 1M = ce mois, 1Y = cette année",
|
||||
"tt_earn_mode_inflows": "Mode de gains : montants reçus (minage inclus)",
|
||||
"tt_earn_mode_net": "Mode de gains : net (reçu moins envoyé)",
|
||||
"tt_earn_show_all": "Afficher le montant des gains pour chaque adresse",
|
||||
"tt_earnings_format": "Comment les montants de gains par adresse et totaux sont affichés",
|
||||
"tt_encrypt": "Chiffrer wallet.dat avec une phrase secrète",
|
||||
"tt_export_all": "Exporter toutes les clés privées dans un fichier",
|
||||
"tt_export_csv": "Exporter l'historique des transactions en feuille de calcul CSV",
|
||||
"tt_export_key": "Exporter la clé privée de l'adresse sélectionnée",
|
||||
"tt_favorite_opacity": "Opacité du bouton étoile des favoris et de son contour doré dans la liste d'adresses (minimum 15 %)",
|
||||
"tt_fetch_prices": "Récupérer les prix du marché DRGX depuis l'API CoinGecko",
|
||||
"tt_font_scale": "Mettre à l'échelle tout le texte et l'interface (1.0x = par défaut, jusqu'à 1.5x).",
|
||||
"tt_idle_delay": "Combien de temps attendre avant de commencer le minage",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "Désactiver tous les effets visuels lourds\\nRaccourci : Ctrl+Shift+Down",
|
||||
"tt_merge": "Consolider plusieurs UTXOs vers une adresse",
|
||||
"tt_mine_idle": "Démarrer le minage automatiquement quand le\\nsystème est inactif (aucune entrée clavier/souris)",
|
||||
"tt_mining_est_daily": "Une moyenne statistique à long terme pour votre part actuelle de hashrate dans le réseau. Les récompenses réelles de minage arrivent au hasard (Poisson) — souvent rien pendant longtemps, puis une somme — ce n'est donc PAS un revenu quotidien régulier.",
|
||||
"tt_mining_est_daily_fee_fmt": "Affiché après les %.1f%% de frais de pool.",
|
||||
"tt_mining_est_daily_nofee": "Affiché avant les frais de pool (frais inconnus pour ce pool).",
|
||||
"tt_new_mining_address": "Crée une adresse transparente (R…) pour les récompenses de minage en solo. Elle ne peut pas recevoir de paiements — partagez une adresse blindée pour cela.",
|
||||
"tt_noise": "Intensité de texture grainée (0% = désactivé, 100% = maximum)",
|
||||
"tt_open_app_dir": "Ouvrir le dossier ObsidianDragon (paramètres, thèmes, journaux) dans le gestionnaire de fichiers",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "Opacité des cartes et de la barre latérale (100% = entièrement opaque, plus bas = plus transparent)",
|
||||
"tt_validate": "Vérifier si une adresse DragonX est valide",
|
||||
"tt_verbose": "Journaliser les diagnostics de connexion détaillés,\\nl'état du daemon et les informations de propriétaire de port\\ndans l'onglet Console",
|
||||
"tt_view_transactions": "Afficher les transactions impliquant cette adresse dans l'onglet Historique",
|
||||
"tt_wallet_size_warn": "Les portefeuilles blindés grandissent avec les données de témoin de chaque note — fusionner de nombreuses notes en une seule adresse réduit cette taille. Sauvegardez d'abord.",
|
||||
"tt_wallets_button": "Répertoriez vos fichiers de portefeuille et passez de l'un à l'autre",
|
||||
"tt_website": "Ouvrir le site web DragonX",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "Tout afficher",
|
||||
"view_details": "Voir les détails",
|
||||
"view_on_explorer": "Voir dans l'explorateur",
|
||||
"view_transactions": "Voir les transactions",
|
||||
"waiting_for_daemon": "En attente de la connexion au daemon...",
|
||||
"wallet": "PORTEFEUILLE",
|
||||
"wallet_degraded_notify": "Votre portefeuille s'est ouvert en mode à fonctionnalités réduites : les fonds existants sont en sécurité et dépensables, mais la création de nouvelles adresses et le blindage sont désactivés. Sauvegardez votre phrase de récupération et restaurez-la pour réparer entièrement le portefeuille.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "ダウンロード",
|
||||
"download_bootstrap": "ブートストラップをダウンロード",
|
||||
"dragonx_green": "DragonX(グリーン)",
|
||||
"earnings_format": "収益の表示形式",
|
||||
"earnings_format_coin": "コインのみ",
|
||||
"earnings_format_coin_fiat": "コイン+法定通貨",
|
||||
"earnings_format_fiat": "法定通貨のみ",
|
||||
"earnings_label": "収益",
|
||||
"edit": "編集",
|
||||
"empty_wallet_keys_suffix": "個の鍵",
|
||||
"empty_wallet_open_manager": "ウォレットマネージャーを開く",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "法定通貨での価格を表示するには?",
|
||||
"faq_w_set_title": "設定と外観",
|
||||
"favorite_address": "お気に入りに追加",
|
||||
"favorite_opacity": "お気に入りハイライトの不透明度",
|
||||
"fee": "手数料",
|
||||
"fee_high": "高い",
|
||||
"fee_label": "手数料:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (非表示)",
|
||||
"hide": "非表示",
|
||||
"hide_address": "アドレスを非表示",
|
||||
"hide_earnings": "収益を非表示",
|
||||
"hide_qr": "QRを非表示",
|
||||
"hide_zero_balances": "残高0を非表示",
|
||||
"history": "履歴",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "予測ブロック",
|
||||
"mining_est_daily": "予測日収",
|
||||
"mining_est_daily_pool_sub": "おおよそのソロ換算(プール手数料前)",
|
||||
"mining_est_daily_sub": "平均",
|
||||
"mining_est_daily_sub_net": "平均・手取り",
|
||||
"mining_filter_all": "すべて",
|
||||
"mining_filter_tip_all": "すべての収益を表示",
|
||||
"mining_filter_tip_pool": "プール収益のみ表示",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "シールド",
|
||||
"shielding_notice": "注意:トランスペアレント (T) アドレスからプライベート (Z) アドレスへ資金をシールドします。",
|
||||
"show": "表示",
|
||||
"show_earnings": "収益を表示",
|
||||
"show_hidden": "非表示を表示 (%d)",
|
||||
"show_qr": "QRを表示",
|
||||
"show_qr_code": "QRコードを表示",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "デバッグログオプションを展開",
|
||||
"tt_delete_blockchain": "すべてのブロックチェーンデータを削除して新規同期を開始します。wallet.dat と設定は保持されます。",
|
||||
"tt_download_bootstrap": "ブロックチェーンブートストラップをダウンロードして同期を高速化\n既存のブロックデータは置き換えられます",
|
||||
"tt_earn_interval": "収益期間:1H=今の時間、1D=今日、1M=今月、1Y=今年",
|
||||
"tt_earn_mode_inflows": "収益モード:受取額(マイニングを含む)",
|
||||
"tt_earn_mode_net": "収益モード:純額(受取から送金を差し引いた額)",
|
||||
"tt_earn_show_all": "すべてのアドレスの収益額を表示",
|
||||
"tt_earnings_format": "アドレスごとおよび合計の収益額の表示方法",
|
||||
"tt_encrypt": "パスフレーズで wallet.dat を暗号化",
|
||||
"tt_export_all": "すべての秘密鍵をファイルにエクスポート",
|
||||
"tt_export_csv": "トランザクション履歴を CSV スプレッドシートとしてエクスポート",
|
||||
"tt_export_key": "選択したアドレスの秘密鍵をエクスポート",
|
||||
"tt_favorite_opacity": "アドレス一覧のお気に入りスターボタンとその金色の枠線の不透明度(最小15%)",
|
||||
"tt_fetch_prices": "CoinGecko API から DRGX の市場価格を取得",
|
||||
"tt_font_scale": "すべてのテキストと UI をスケーリング(1.0x = デフォルト、最大 1.5x)。",
|
||||
"tt_idle_delay": "マイニング開始前の待機時間",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "すべての重い視覚効果を無効化\\nホットキー:Ctrl+Shift+Down",
|
||||
"tt_merge": "複数の UTXO を一つのアドレスに統合",
|
||||
"tt_mine_idle": "システムがアイドル状態(キーボード/マウス入力なし)\\nのとき自動的にマイニングを開始",
|
||||
"tt_mining_est_daily": "ネットワーク全体に占める現在のハッシュレート比率に基づく長期的な統計平均です。実際のマイニング報酬はランダム(ポアソン分布)に発生し、長期間ゼロが続いた後にまとまって得られることが多いため、これは安定した日次収入ではありません。",
|
||||
"tt_mining_est_daily_fee_fmt": "%.1f%% のプール手数料を差し引いた値です。",
|
||||
"tt_mining_est_daily_nofee": "プール手数料を差し引く前の値です(このプールの手数料は不明)。",
|
||||
"tt_new_mining_address": "ソロマイニング報酬用の透明(R…)アドレスを作成します。支払いは受け取れません。支払いにはシールドアドレスを共有してください。",
|
||||
"tt_noise": "グレインテクスチャ強度(0% = オフ、100% = 最大)",
|
||||
"tt_open_app_dir": "ObsidianDragon フォルダ(設定、テーマ、ログ)をファイルマネージャーで開く",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "カードとサイドバーの不透明度(100% = 完全不透明、低い = より透過)",
|
||||
"tt_validate": "DragonX アドレスが有効かどうかを確認",
|
||||
"tt_verbose": "詳細な接続診断、デーモン状態、\\nポート所有者情報をコンソールタブに記録",
|
||||
"tt_view_transactions": "このアドレスに関連する取引を履歴タブに表示",
|
||||
"tt_wallet_size_warn": "シールドウォレットはノートごとのウィットネスデータによって肥大化します — 多数のノートを1つのアドレスに統合するとサイズを抑えられます。事前にバックアップしてください。",
|
||||
"tt_wallets_button": "ウォレットファイルを一覧表示して切り替えます",
|
||||
"tt_website": "DragonX ウェブサイトを開く",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "すべて表示",
|
||||
"view_details": "詳細を表示",
|
||||
"view_on_explorer": "エクスプローラーで表示",
|
||||
"view_transactions": "取引を表示",
|
||||
"waiting_for_daemon": "デーモン接続を待機中...",
|
||||
"wallet": "ウォレット",
|
||||
"wallet_degraded_notify": "ウォレットは機能制限モードで開かれました。既存の資金は安全で使用可能ですが、新しいアドレスの作成とシールドは無効になっています。ウォレットを完全に修復するには、シードフレーズをバックアップしてそこから復元してください。",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "다운로드",
|
||||
"download_bootstrap": "부트스트랩 다운로드",
|
||||
"dragonx_green": "DragonX(그린)",
|
||||
"earnings_format": "수익 표시 형식",
|
||||
"earnings_format_coin": "코인만",
|
||||
"earnings_format_coin_fiat": "코인 + 법정화폐",
|
||||
"earnings_format_fiat": "법정화폐만",
|
||||
"earnings_label": "수익",
|
||||
"edit": "편집",
|
||||
"empty_wallet_keys_suffix": "개 키",
|
||||
"empty_wallet_open_manager": "지갑 관리자 열기",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "법정화폐 가격을 어떻게 표시하나요?",
|
||||
"faq_w_set_title": "설정 및 화면",
|
||||
"favorite_address": "즐겨찾기에 추가",
|
||||
"favorite_opacity": "즐겨찾기 강조 불투명도",
|
||||
"fee": "수수료",
|
||||
"fee_high": "높음",
|
||||
"fee_label": "수수료:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (숨김)",
|
||||
"hide": "숨기기",
|
||||
"hide_address": "주소 숨기기",
|
||||
"hide_earnings": "수익 숨기기",
|
||||
"hide_qr": "QR 숨기기",
|
||||
"hide_zero_balances": "잔액 0 숨기기",
|
||||
"history": "내역",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "예상 블록",
|
||||
"mining_est_daily": "예상 일일 수익",
|
||||
"mining_est_daily_pool_sub": "대략적인 솔로 환산, 풀 수수료 전",
|
||||
"mining_est_daily_sub": "평균",
|
||||
"mining_est_daily_sub_net": "평균, 순",
|
||||
"mining_filter_all": "전체",
|
||||
"mining_filter_tip_all": "모든 수익 표시",
|
||||
"mining_filter_tip_pool": "풀 수익만 표시",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "차폐",
|
||||
"shielding_notice": "참고: 투명 (T) 주소에서 프라이빗 (Z) 주소로 자금을 실딩합니다.",
|
||||
"show": "표시",
|
||||
"show_earnings": "수익 표시",
|
||||
"show_hidden": "숨겨진 항목 표시 (%d)",
|
||||
"show_qr": "QR 표시",
|
||||
"show_qr_code": "QR 코드 표시",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "디버그 로깅 옵션 펼치기",
|
||||
"tt_delete_blockchain": "모든 블록체인 데이터를 삭제하고 새로 동기화합니다. wallet.dat 및 설정은 보존됩니다.",
|
||||
"tt_download_bootstrap": "블록체인 부트스트랩을 다운로드하여 동기화 가속\n기존 블록 데이터가 교체됩니다",
|
||||
"tt_earn_interval": "수익 기간: 1H = 현재 시간, 1D = 오늘, 1M = 이번 달, 1Y = 올해",
|
||||
"tt_earn_mode_inflows": "수익 모드: 받은 금액(채굴 포함)",
|
||||
"tt_earn_mode_net": "수익 모드: 순액(받은 금액에서 보낸 금액을 뺀 값)",
|
||||
"tt_earn_show_all": "모든 주소의 수익 금액 표시",
|
||||
"tt_earnings_format": "주소별 및 합계 수익 금액의 표시 방식",
|
||||
"tt_encrypt": "비밀번호로 wallet.dat 암호화",
|
||||
"tt_export_all": "모든 개인키를 파일로 내보내기",
|
||||
"tt_export_csv": "거래 내역을 CSV 스프레드시트로 내보내기",
|
||||
"tt_export_key": "선택한 주소의 개인키 내보내기",
|
||||
"tt_favorite_opacity": "주소 목록에서 즐겨찾기 별 버튼과 금색 테두리의 불투명도(최소 15%)",
|
||||
"tt_fetch_prices": "CoinGecko API에서 DRGX 시장 가격 가져오기",
|
||||
"tt_font_scale": "모든 텍스트 및 UI 크기 조정 (1.0x = 기본, 최대 1.5x).",
|
||||
"tt_idle_delay": "채굴 시작 전 대기 시간",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "모든 고부하 시각 효과 비활성화\\n단축키: Ctrl+Shift+Down",
|
||||
"tt_merge": "여러 UTXO를 하나의 주소로 통합",
|
||||
"tt_mine_idle": "시스템이 유휴 상태(키보드/마우스 입력 없음)일 때\\n자동으로 채굴 시작",
|
||||
"tt_mining_est_daily": "네트워크에서 현재 해시레이트 비중에 대한 장기 통계 평균입니다. 실제 채굴 보상은 무작위(푸아송)로 도착하여 오랫동안 아무것도 없다가 한꺼번에 들어오는 경우가 많으므로, 이는 꾸준한 일일 수입이 아닙니다.",
|
||||
"tt_mining_est_daily_fee_fmt": "%.1f%% 풀 수수료를 제외하고 표시됩니다.",
|
||||
"tt_mining_est_daily_nofee": "풀 수수료를 제외하기 전 값입니다(이 풀의 수수료는 알 수 없음).",
|
||||
"tt_new_mining_address": "솔로 채굴 보상용 투명(R…) 주소를 만듭니다. 결제는 받을 수 없으니, 결제용으로는 차폐 주소를 공유하세요.",
|
||||
"tt_noise": "그레인 텍스처 강도 (0% = 끔, 100% = 최대)",
|
||||
"tt_open_app_dir": "파일 관리자에서 ObsidianDragon 폴더(설정, 테마, 로그)를 엽니다",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "카드 및 사이드바 불투명도 (100% = 완전 불투명, 낮을수록 더 투명)",
|
||||
"tt_validate": "DragonX 주소가 유효한지 확인",
|
||||
"tt_verbose": "콘솔 탭에 상세 연결 진단,\\n데몬 상태 및 포트 소유자 정보 기록",
|
||||
"tt_view_transactions": "이 주소와 관련된 거래를 기록 탭에 표시",
|
||||
"tt_wallet_size_warn": "보호 지갑은 각 노트의 위트니스 데이터로 인해 커집니다 — 여러 노트를 하나의 주소로 병합하면 크기를 줄일 수 있습니다. 먼저 백업하세요.",
|
||||
"tt_wallets_button": "지갑 파일 목록을 보고 전환합니다",
|
||||
"tt_website": "DragonX 웹사이트 열기",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "모두 보기",
|
||||
"view_details": "상세 보기",
|
||||
"view_on_explorer": "탐색기에서 보기",
|
||||
"view_transactions": "거래 보기",
|
||||
"waiting_for_daemon": "데몬 연결 대기 중...",
|
||||
"wallet": "지갑",
|
||||
"wallet_degraded_notify": "지갑이 기능 제한 모드로 열렸습니다. 기존 자금은 안전하며 사용할 수 있지만, 새 주소 생성과 보호 처리는 비활성화되어 있습니다. 시드 문구를 백업하고 이를 통해 복원하면 지갑을 완전히 복구할 수 있습니다.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "Baixar",
|
||||
"download_bootstrap": "Baixar Bootstrap",
|
||||
"dragonx_green": "DragonX (Verde)",
|
||||
"earnings_format": "Formato de exibição de ganhos",
|
||||
"earnings_format_coin": "Apenas moeda",
|
||||
"earnings_format_coin_fiat": "Moeda + fiduciária",
|
||||
"earnings_format_fiat": "Apenas fiduciária",
|
||||
"earnings_label": "Ganhos",
|
||||
"edit": "Editar",
|
||||
"empty_wallet_keys_suffix": "chaves",
|
||||
"empty_wallet_open_manager": "Abrir gerenciador de carteiras",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "Como exibo preços em moeda fiduciária?",
|
||||
"faq_w_set_title": "Configurações e aparência",
|
||||
"favorite_address": "Adicionar aos favoritos",
|
||||
"favorite_opacity": "Opacidade do destaque de favoritos",
|
||||
"fee": "Taxa",
|
||||
"fee_high": "Alta",
|
||||
"fee_label": "Taxa:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (oculto)",
|
||||
"hide": "Ocultar",
|
||||
"hide_address": "Ocultar endereço",
|
||||
"hide_earnings": "Ocultar ganhos",
|
||||
"hide_qr": "Ocultar QR",
|
||||
"hide_zero_balances": "Ocultar saldos zero",
|
||||
"history": "Histórico",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "Bloco Est.",
|
||||
"mining_est_daily": "Est. Diário",
|
||||
"mining_est_daily_pool_sub": "equivalente solo aproximado, antes da taxa do pool",
|
||||
"mining_est_daily_sub": "méd.",
|
||||
"mining_est_daily_sub_net": "méd., líq.",
|
||||
"mining_filter_all": "Todos",
|
||||
"mining_filter_tip_all": "Mostrar todos os ganhos",
|
||||
"mining_filter_tip_pool": "Mostrar apenas ganhos do pool",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "Blindado",
|
||||
"shielding_notice": "Nota: Isso blindará fundos de um endereço transparente (T) para um endereço privado (Z).",
|
||||
"show": "Mostrar",
|
||||
"show_earnings": "Mostrar ganhos",
|
||||
"show_hidden": "Mostrar ocultos (%d)",
|
||||
"show_qr": "Mostrar QR",
|
||||
"show_qr_code": "Mostrar Código QR",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "Expandir opções de registro de depuração",
|
||||
"tt_delete_blockchain": "Excluir todos os dados da blockchain e iniciar uma nova sincronização. wallet.dat e configuração são preservados.",
|
||||
"tt_download_bootstrap": "Baixar bootstrap da blockchain para acelerar a sincronização\nDados de blocos existentes serão substituídos",
|
||||
"tt_earn_interval": "Período de ganhos: 1H = esta hora, 1D = hoje, 1M = este mês, 1Y = este ano",
|
||||
"tt_earn_mode_inflows": "Modo de ganhos: valores recebidos (incluindo mineração)",
|
||||
"tt_earn_mode_net": "Modo de ganhos: líquido (recebido menos enviado)",
|
||||
"tt_earn_show_all": "Mostrar o valor de ganhos para cada endereço",
|
||||
"tt_earnings_format": "Como os valores de ganhos por endereço e totais são exibidos",
|
||||
"tt_encrypt": "Encriptar wallet.dat com uma frase secreta",
|
||||
"tt_export_all": "Exportar todas as chaves privadas para um arquivo",
|
||||
"tt_export_csv": "Exportar histórico de transações como planilha CSV",
|
||||
"tt_export_key": "Exportar a chave privada do endereço selecionado",
|
||||
"tt_favorite_opacity": "Opacidade do botão de estrela de favoritos e do seu contorno dourado na lista de endereços (mínimo 15%)",
|
||||
"tt_fetch_prices": "Obter preços de mercado DRGX da API CoinGecko",
|
||||
"tt_font_scale": "Escalar todo o texto e interface (1.0x = padrão, até 1.5x).",
|
||||
"tt_idle_delay": "Quanto tempo esperar antes de iniciar a mineração",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "Desativar todos os efeitos visuais pesados\\nAtalho: Ctrl+Shift+Down",
|
||||
"tt_merge": "Consolidar múltiplos UTXOs em um endereço",
|
||||
"tt_mine_idle": "Iniciar mineração automaticamente quando o\\nsistema estiver ocioso (sem entrada de teclado/mouse)",
|
||||
"tt_mining_est_daily": "Uma média estatística de longo prazo para a sua fração atual de hashrate na rede. As recompensas reais de mineração chegam de forma aleatória (Poisson) — muitas vezes nada por longos períodos e depois um valor — por isso NÃO é uma renda diária constante.",
|
||||
"tt_mining_est_daily_fee_fmt": "Mostrado após a taxa de %.1f%% do pool.",
|
||||
"tt_mining_est_daily_nofee": "Mostrado antes da taxa do pool (taxa desconhecida para este pool).",
|
||||
"tt_new_mining_address": "Cria um endereço transparente (R…) para recompensas de mineração solo. Ele não pode receber pagamentos — compartilhe um endereço blindado para isso.",
|
||||
"tt_noise": "Intensidade de textura granulada (0% = desligado, 100% = máximo)",
|
||||
"tt_open_app_dir": "Abrir a pasta ObsidianDragon (configurações, temas, logs) no gerenciador de arquivos",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "Opacidade de cartões e barra lateral (100% = totalmente opaco, menor = mais transparente)",
|
||||
"tt_validate": "Verificar se um endereço DragonX é válido",
|
||||
"tt_verbose": "Registrar diagnósticos detalhados de conexão,\\nestado do daemon e info de proprietário de porta\\nna aba Console",
|
||||
"tt_view_transactions": "Mostrar as transações que envolvem este endereço na aba Histórico",
|
||||
"tt_wallet_size_warn": "Carteiras blindadas crescem com os dados de testemunha de cada nota — mesclar várias notas em um único endereço reduz esse tamanho. Faça backup antes.",
|
||||
"tt_wallets_button": "Liste os arquivos de carteira e alterne entre eles",
|
||||
"tt_website": "Abrir o site do DragonX",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "Ver Tudo",
|
||||
"view_details": "Ver Detalhes",
|
||||
"view_on_explorer": "Ver no Explorador",
|
||||
"view_transactions": "Ver transações",
|
||||
"waiting_for_daemon": "Aguardando conexão com o daemon...",
|
||||
"wallet": "CARTEIRA",
|
||||
"wallet_degraded_notify": "Sua carteira foi aberta em modo de função reduzida: os fundos existentes estão seguros e disponíveis para gasto, mas a criação de novos endereços e o blindamento estão desativados. Faça backup da sua frase de recuperação e restaure-a para reparar totalmente a carteira.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "Скачать",
|
||||
"download_bootstrap": "Скачать бутстрап",
|
||||
"dragonx_green": "DragonX (Зелёная)",
|
||||
"earnings_format": "Формат отображения дохода",
|
||||
"earnings_format_coin": "Только монета",
|
||||
"earnings_format_coin_fiat": "Монета + фиат",
|
||||
"earnings_format_fiat": "Только фиат",
|
||||
"earnings_label": "Доход",
|
||||
"edit": "Редактировать",
|
||||
"empty_wallet_keys_suffix": "ключей",
|
||||
"empty_wallet_open_manager": "Открыть менеджер кошельков",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "Как показать цены в фиатной валюте?",
|
||||
"faq_w_set_title": "Настройки и внешний вид",
|
||||
"favorite_address": "Добавить в избранное",
|
||||
"favorite_opacity": "Непрозрачность выделения избранного",
|
||||
"fee": "Комиссия",
|
||||
"fee_high": "Высокая",
|
||||
"fee_label": "Комиссия:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (скрыт)",
|
||||
"hide": "Скрыть",
|
||||
"hide_address": "Скрыть адрес",
|
||||
"hide_earnings": "Скрыть доход",
|
||||
"hide_qr": "Скрыть QR",
|
||||
"hide_zero_balances": "Скрыть нулевые балансы",
|
||||
"history": "История",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "Расч. блок",
|
||||
"mining_est_daily": "Расч. за день",
|
||||
"mining_est_daily_pool_sub": "примерный соло-эквивалент, до комиссии пула",
|
||||
"mining_est_daily_sub": "сред.",
|
||||
"mining_est_daily_sub_net": "сред., нетто",
|
||||
"mining_filter_all": "Все",
|
||||
"mining_filter_tip_all": "Показать все доходы",
|
||||
"mining_filter_tip_pool": "Показать только доходы пула",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "Экранированный",
|
||||
"shielding_notice": "Примечание: это переведёт средства с прозрачного (T) адреса на приватный (Z) адрес.",
|
||||
"show": "Показать",
|
||||
"show_earnings": "Показать доход",
|
||||
"show_hidden": "Показать скрытые (%d)",
|
||||
"show_qr": "Показать QR",
|
||||
"show_qr_code": "Показать QR-код",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "Развернуть параметры журнала отладки",
|
||||
"tt_delete_blockchain": "Удалить все данные блокчейна и начать синхронизацию заново. wallet.dat и конфигурация сохраняются.",
|
||||
"tt_download_bootstrap": "Скачать бутстрап блокчейна для ускорения синхронизации\nСуществующие данные блоков будут заменены",
|
||||
"tt_earn_interval": "Период дохода: 1H — текущий час, 1D — сегодня, 1M — текущий месяц, 1Y — текущий год",
|
||||
"tt_earn_mode_inflows": "Режим дохода: полученные суммы (включая майнинг)",
|
||||
"tt_earn_mode_net": "Режим дохода: нетто (получено минус отправлено)",
|
||||
"tt_earn_show_all": "Показывать сумму дохода для каждого адреса",
|
||||
"tt_earnings_format": "Как отображаются суммы дохода по адресам и итогам",
|
||||
"tt_encrypt": "Зашифровать wallet.dat паролем",
|
||||
"tt_export_all": "Экспортировать все приватные ключи в файл",
|
||||
"tt_export_csv": "Экспортировать историю транзакций в виде таблицы CSV",
|
||||
"tt_export_key": "Экспортировать приватный ключ выбранного адреса",
|
||||
"tt_favorite_opacity": "Непрозрачность кнопки-звёздочки избранного и её золотой обводки в списке адресов (минимум 15 %)",
|
||||
"tt_fetch_prices": "Получить рыночные цены DRGX из API CoinGecko",
|
||||
"tt_font_scale": "Масштабировать весь текст и интерфейс (1.0x = по умолчанию, до 1.5x).",
|
||||
"tt_idle_delay": "Сколько ждать перед началом майнинга",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "Отключить все тяжёлые визуальные эффекты\\nГорячая клавиша: Ctrl+Shift+Down",
|
||||
"tt_merge": "Объединить несколько UTXO в один адрес",
|
||||
"tt_mine_idle": "Автоматически начать майнинг при\\nпростое системы (нет ввода с клавиатуры/мыши)",
|
||||
"tt_mining_est_daily": "Долгосрочное статистическое среднее для вашей текущей доли хешрейта в сети. Реальные награды за майнинг приходят случайно (по Пуассону) — часто подолгу ничего, а затем сразу много, — поэтому это НЕ стабильный ежедневный доход.",
|
||||
"tt_mining_est_daily_fee_fmt": "Показано за вычетом комиссии пула %.1f%%.",
|
||||
"tt_mining_est_daily_nofee": "Показано до вычета комиссии пула (комиссия этого пула неизвестна).",
|
||||
"tt_new_mining_address": "Создаёт прозрачный адрес (R…) для наград за соло-майнинг. Он не принимает платежи — для них сообщайте экранированный адрес.",
|
||||
"tt_noise": "Интенсивность зернистой текстуры (0% = выкл., 100% = максимум)",
|
||||
"tt_open_app_dir": "Открыть папку ObsidianDragon (настройки, темы, логи) в файловом менеджере",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "Непрозрачность карточек и боковой панели (100% = полностью непрозрачно, ниже = прозрачнее)",
|
||||
"tt_validate": "Проверить, действителен ли адрес DragonX",
|
||||
"tt_verbose": "Записывать подробную диагностику подключений,\\nсостояние демона и информацию о владельце порта\\nна вкладке Консоль",
|
||||
"tt_view_transactions": "Показать транзакции с этим адресом на вкладке «История»",
|
||||
"tt_wallet_size_warn": "Защищённые кошельки растут за счёт данных witness каждой записи — объединение множества записей в один адрес уменьшает его размер. Сначала сделайте резервную копию.",
|
||||
"tt_wallets_button": "Показать файлы кошельков и переключаться между ними",
|
||||
"tt_website": "Открыть сайт DragonX",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "Показать всё",
|
||||
"view_details": "Подробнее",
|
||||
"view_on_explorer": "Посмотреть в обозревателе",
|
||||
"view_transactions": "Показать транзакции",
|
||||
"waiting_for_daemon": "Ожидание подключения к daemon...",
|
||||
"wallet": "КОШЕЛЁК",
|
||||
"wallet_degraded_notify": "Ваш кошелёк открыт в режиме с ограниченной функциональностью: существующие средства в безопасности и доступны для расходования, но создание новых адресов и защита средств отключены. Сделайте резервную копию сид-фразы и восстановите из неё кошелёк, чтобы полностью его исправить.",
|
||||
|
||||
@@ -639,6 +639,11 @@
|
||||
"download": "下载",
|
||||
"download_bootstrap": "下载引导程序",
|
||||
"dragonx_green": "DragonX(绿色)",
|
||||
"earnings_format": "收益显示格式",
|
||||
"earnings_format_coin": "仅代币",
|
||||
"earnings_format_coin_fiat": "代币+法币",
|
||||
"earnings_format_fiat": "仅法币",
|
||||
"earnings_label": "收益",
|
||||
"edit": "编辑",
|
||||
"empty_wallet_keys_suffix": "个密钥",
|
||||
"empty_wallet_open_manager": "打开钱包管理器",
|
||||
@@ -863,6 +868,7 @@
|
||||
"faq_w_set_4_q": "如何显示法币价格?",
|
||||
"faq_w_set_title": "设置与外观",
|
||||
"favorite_address": "收藏地址",
|
||||
"favorite_opacity": "收藏高亮不透明度",
|
||||
"fee": "手续费",
|
||||
"fee_high": "高",
|
||||
"fee_label": "手续费:",
|
||||
@@ -941,6 +947,7 @@
|
||||
"hidden_tag": " (已隐藏)",
|
||||
"hide": "隐藏",
|
||||
"hide_address": "隐藏地址",
|
||||
"hide_earnings": "隐藏收益",
|
||||
"hide_qr": "隐藏二维码",
|
||||
"hide_zero_balances": "隐藏零余额",
|
||||
"history": "历史",
|
||||
@@ -1307,6 +1314,8 @@
|
||||
"mining_est_block": "预计区块",
|
||||
"mining_est_daily": "预计日收益",
|
||||
"mining_est_daily_pool_sub": "粗略的单人挖矿等值,扣除矿池费用前",
|
||||
"mining_est_daily_sub": "平均",
|
||||
"mining_est_daily_sub_net": "平均·净",
|
||||
"mining_filter_all": "全部",
|
||||
"mining_filter_tip_all": "显示所有收益",
|
||||
"mining_filter_tip_pool": "仅显示矿池收益",
|
||||
@@ -1917,6 +1926,7 @@
|
||||
"shielded_type": "屏蔽",
|
||||
"shielding_notice": "注意:这将把资金从透明 (T) 地址转移到隐私 (Z) 地址。",
|
||||
"show": "显示",
|
||||
"show_earnings": "显示收益",
|
||||
"show_hidden": "显示已隐藏 (%d)",
|
||||
"show_qr": "显示二维码",
|
||||
"show_qr_code": "显示二维码",
|
||||
@@ -2065,10 +2075,16 @@
|
||||
"tt_debug_expand": "展开调试日志选项",
|
||||
"tt_delete_blockchain": "删除所有区块链数据并重新同步。wallet.dat 和配置将被保留。",
|
||||
"tt_download_bootstrap": "下载区块链引导程序以加速同步\n现有区块数据将被替换",
|
||||
"tt_earn_interval": "收益周期:1H=本小时,1D=今天,1M=本月,1Y=今年",
|
||||
"tt_earn_mode_inflows": "收益模式:收到的金额(含挖矿)",
|
||||
"tt_earn_mode_net": "收益模式:净额(收到减去发送)",
|
||||
"tt_earn_show_all": "为每个地址显示收益数值",
|
||||
"tt_earnings_format": "按地址和总计的收益数值的显示方式",
|
||||
"tt_encrypt": "使用密码加密 wallet.dat",
|
||||
"tt_export_all": "将所有私钥导出到文件",
|
||||
"tt_export_csv": "将交易历史导出为 CSV 电子表格",
|
||||
"tt_export_key": "导出所选地址的私钥",
|
||||
"tt_favorite_opacity": "地址列表中收藏星形按钮及其金色轮廓的不透明度(最低 15%)",
|
||||
"tt_fetch_prices": "从 CoinGecko API 获取 DRGX 市场价格",
|
||||
"tt_font_scale": "缩放所有文本和界面(1.0x = 默认,最大 1.5x)。",
|
||||
"tt_idle_delay": "开始挖矿前等待多长时间",
|
||||
@@ -2106,6 +2122,9 @@
|
||||
"tt_low_spec": "禁用所有重度视觉效果\\n快捷键:Ctrl+Shift+Down",
|
||||
"tt_merge": "将多个 UTXO 合并到一个地址",
|
||||
"tt_mine_idle": "系统空闲时自动开始挖矿\\n(无键盘/鼠标输入)",
|
||||
"tt_mining_est_daily": "基于您当前在全网算力占比的长期统计平均值。实际挖矿奖励是随机到达的(泊松分布)——常常很长时间没有,然后一次性获得一笔,因此这并不是稳定的每日收入。",
|
||||
"tt_mining_est_daily_fee_fmt": "已扣除 %.1f%% 矿池手续费。",
|
||||
"tt_mining_est_daily_nofee": "未扣除矿池手续费(此矿池手续费未知)。",
|
||||
"tt_new_mining_address": "创建一个用于单人挖矿奖励的透明(R…)地址。它无法接收付款——收款请提供屏蔽地址。",
|
||||
"tt_noise": "颗粒纹理强度(0% = 关闭,100% = 最大)",
|
||||
"tt_open_app_dir": "在文件管理器中打开 ObsidianDragon 文件夹(设置、主题、日志)",
|
||||
@@ -2154,6 +2173,7 @@
|
||||
"tt_ui_opacity": "卡片和侧边栏不透明度(100% = 完全不透明,越低越透明)",
|
||||
"tt_validate": "检查 DragonX 地址是否有效",
|
||||
"tt_verbose": "将详细连接诊断、守护进程状态\\n和端口所有者信息记录到控制台选项卡",
|
||||
"tt_view_transactions": "在历史记录标签页中显示涉及此地址的交易",
|
||||
"tt_wallet_size_warn": "屏蔽钱包会随每个票据的见证数据增长——将多个票据合并到一个地址可以缩小体积。请先备份。",
|
||||
"tt_wallets_button": "列出您的钱包文件并在它们之间切换",
|
||||
"tt_website": "打开 DragonX 网站",
|
||||
@@ -2220,6 +2240,7 @@
|
||||
"view_all": "查看全部",
|
||||
"view_details": "查看详情",
|
||||
"view_on_explorer": "在浏览器中查看",
|
||||
"view_transactions": "查看交易",
|
||||
"waiting_for_daemon": "等待守护进程连接...",
|
||||
"wallet": "钱包",
|
||||
"wallet_degraded_notify": "您的钱包以受限功能模式打开:现有资金安全且可用,但无法创建新地址或进行屏蔽操作。请备份助记词并用它恢复钱包以完全修复。",
|
||||
|
||||
@@ -204,6 +204,8 @@ bool Settings::load(const std::string& path)
|
||||
ui_opacity_ = 1.0f; // legacy: reduced = fully opaque
|
||||
}
|
||||
loadScalar(j, "window_opacity", window_opacity_);
|
||||
loadScalar(j, "favorite_opacity", favorite_opacity_);
|
||||
favorite_opacity_ = std::clamp(favorite_opacity_, 0.15f, 1.0f); // matches setFavoriteOpacity()
|
||||
if (j.contains("balance_layout")) {
|
||||
if (j["balance_layout"].is_string())
|
||||
balance_layout_ = j["balance_layout"].get<std::string>();
|
||||
@@ -240,6 +242,18 @@ bool Settings::load(const std::string& path)
|
||||
for (const auto& a : j["favorite_addresses"])
|
||||
if (a.is_string()) favorite_addresses_.insert(a.get<std::string>());
|
||||
}
|
||||
if (j.contains("earnings_addresses") && j["earnings_addresses"].is_array()) {
|
||||
earnings_addresses_.clear();
|
||||
for (const auto& a : j["earnings_addresses"])
|
||||
if (a.is_string()) earnings_addresses_.insert(a.get<std::string>());
|
||||
}
|
||||
loadScalar(j, "earnings_interval", earnings_interval_);
|
||||
earnings_interval_ = std::clamp(earnings_interval_, 0, 3);
|
||||
loadScalar(j, "earnings_mode", earnings_mode_);
|
||||
earnings_mode_ = std::clamp(earnings_mode_, 0, 1);
|
||||
loadScalar(j, "earnings_format", earnings_format_);
|
||||
earnings_format_ = std::clamp(earnings_format_, 0, 2);
|
||||
loadScalar(j, "earnings_show_all", earnings_show_all_);
|
||||
if (j.contains("address_meta") && j["address_meta"].is_object()) {
|
||||
address_meta_.clear();
|
||||
for (auto& [addr, meta] : j["address_meta"].items()) {
|
||||
@@ -542,6 +556,7 @@ bool Settings::save(const std::string& path)
|
||||
j["gradient_background"] = gradient_background_;
|
||||
j["ui_opacity"] = ui_opacity_;
|
||||
j["window_opacity"] = window_opacity_;
|
||||
j["favorite_opacity"] = favorite_opacity_;
|
||||
j["balance_layout"] = balance_layout_; // saved as string ID
|
||||
j["portfolio_style"] = portfolio_style_;
|
||||
j["contacts_view_mode"] = contacts_view_mode_;
|
||||
@@ -559,6 +574,13 @@ bool Settings::save(const std::string& path)
|
||||
j["favorite_addresses"] = json::array();
|
||||
for (const auto& addr : favorite_addresses_)
|
||||
j["favorite_addresses"].push_back(addr);
|
||||
j["earnings_addresses"] = json::array();
|
||||
for (const auto& addr : earnings_addresses_)
|
||||
j["earnings_addresses"].push_back(addr);
|
||||
j["earnings_interval"] = earnings_interval_;
|
||||
j["earnings_mode"] = earnings_mode_;
|
||||
j["earnings_format"] = earnings_format_;
|
||||
j["earnings_show_all"] = earnings_show_all_;
|
||||
{
|
||||
json meta_obj = json::object();
|
||||
for (const auto& [addr, m] : address_meta_) {
|
||||
|
||||
@@ -261,6 +261,11 @@ public:
|
||||
float getNoiseOpacity() const { return noise_opacity_; }
|
||||
void setNoiseOpacity(float v) { noise_opacity_ = v; }
|
||||
|
||||
// Opacity multiplier for the favorite gold visuals (row outline + star button) in the
|
||||
// Balance address list. 1.0 = theme default (unchanged); floor 0.15 so it can't vanish.
|
||||
float getFavoriteOpacity() const { return favorite_opacity_; }
|
||||
void setFavoriteOpacity(float v) { favorite_opacity_ = std::max(0.15f, std::min(1.0f, v)); }
|
||||
|
||||
// Gradient background mode (use gradient variant of theme background)
|
||||
bool getGradientBackground() const { return gradient_background_; }
|
||||
void setGradientBackground(bool v) { gradient_background_ = v; }
|
||||
@@ -316,6 +321,22 @@ public:
|
||||
void unfavoriteAddress(const std::string& addr) { favorite_addresses_.erase(addr); }
|
||||
int getFavoriteAddressCount() const { return (int)favorite_addresses_.size(); }
|
||||
|
||||
// Inline period-earnings (Balance tab). interval: 0=Hour 1=Day 2=Month 3=Year; mode: 0=Inflows
|
||||
// 1=Net; format: 0=DRGX 1=DRGX+fiat 2=Fiat. show-all toggles the per-address earnings column for
|
||||
// every address at once; earnings_addresses_ is the opt-in set shown when show-all is off.
|
||||
int getEarningsInterval() const { return earnings_interval_; }
|
||||
void setEarningsInterval(int v) { earnings_interval_ = (v < 0 ? 0 : (v > 3 ? 3 : v)); }
|
||||
int getEarningsMode() const { return earnings_mode_; }
|
||||
void setEarningsMode(int v) { earnings_mode_ = (v < 0 ? 0 : (v > 1 ? 1 : v)); }
|
||||
int getEarningsFormat() const { return earnings_format_; }
|
||||
void setEarningsFormat(int v) { earnings_format_ = (v < 0 ? 0 : (v > 2 ? 2 : v)); }
|
||||
bool getEarningsShowAll() const { return earnings_show_all_; }
|
||||
void setEarningsShowAll(bool v) { earnings_show_all_ = v; }
|
||||
const std::set<std::string>& getEarningsAddresses() const { return earnings_addresses_; }
|
||||
bool isAddressEarningsShown(const std::string& addr) const { return earnings_addresses_.count(addr) > 0; }
|
||||
void showAddressEarnings(const std::string& addr) { earnings_addresses_.insert(addr); }
|
||||
void hideAddressEarnings(const std::string& addr) { earnings_addresses_.erase(addr); }
|
||||
|
||||
// Address metadata (labels, icons, custom ordering)
|
||||
struct AddressMeta {
|
||||
std::string label;
|
||||
@@ -685,6 +706,7 @@ private:
|
||||
int acrylic_quality_ = 2;
|
||||
float blur_multiplier_ = 0.10f;
|
||||
float noise_opacity_ = 0.5f;
|
||||
float favorite_opacity_ = 1.0f; // Favorite gold outline/star opacity multiplier (0.15–1.0, 1.0 = theme default)
|
||||
bool gradient_background_ = false;
|
||||
#ifdef _WIN32
|
||||
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque)
|
||||
@@ -706,6 +728,11 @@ private:
|
||||
bool console_auto_focus_ = false; // focus the command input when the Console tab is opened (opt-in)
|
||||
std::set<std::string> hidden_addresses_;
|
||||
std::set<std::string> favorite_addresses_;
|
||||
std::set<std::string> earnings_addresses_; // per-address earnings column opt-in (when show-all off)
|
||||
int earnings_interval_ = 1; // 0=Hour 1=Day 2=Month 3=Year
|
||||
int earnings_mode_ = 0; // 0=Inflows 1=Net
|
||||
int earnings_format_ = 1; // 0=DRGX 1=DRGX+fiat 2=Fiat
|
||||
bool earnings_show_all_ = false;
|
||||
std::map<std::string, AddressMeta> address_meta_;
|
||||
bool wizard_completed_ = false;
|
||||
bool seed_backup_reminded_ = false;
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
// Pure period-earnings aggregation for the Balance tab's inline earnings figures. Header-only and
|
||||
// dependency-free (beyond TransactionInfo + the std lib) so it can be unit-tested without the GUI.
|
||||
//
|
||||
// It sums wallet INFLOWS (received + mined) over the CURRENT calendar period (hour / day / month /
|
||||
// year), optionally NET of sends, both per receiving address and wallet-wide. Values are "as seen"
|
||||
// (no confirmation/maturity gate), matching the balances the wallet already shows.
|
||||
//
|
||||
// Attribution is best-effort: inflows attribute to TransactionInfo::address (our receiving address);
|
||||
// in Net mode a send debits TransactionInfo::from_address when present (our source address). A send
|
||||
// with no from_address still counts in the wallet TOTAL but not in any per-address figure, so the
|
||||
// per-address numbers can be less complete than the total in Net mode.
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "data/wallet_state.h" // TransactionInfo
|
||||
|
||||
namespace dragonx {
|
||||
namespace data {
|
||||
|
||||
enum class EarningsInterval { Hour = 0, Day = 1, Month = 2, Year = 3 };
|
||||
enum class EarningsMode { Inflows = 0, Net = 1 };
|
||||
|
||||
// Start (Unix seconds, local calendar) of the current period containing `nowSec` for `interval`:
|
||||
// Hour = top of the current clock hour; Day = local midnight; Month/Year = the 1st of the local
|
||||
// calendar month / Jan 1 of the local year. DST quirks follow std::mktime (as LocalDayStartMemo does).
|
||||
inline std::int64_t earningsPeriodStart(std::int64_t nowSec, EarningsInterval interval) {
|
||||
std::time_t t = (std::time_t)nowSec;
|
||||
std::tm lt{};
|
||||
#if defined(_WIN32)
|
||||
localtime_s(<, &t);
|
||||
#else
|
||||
localtime_r(&t, <);
|
||||
#endif
|
||||
lt.tm_sec = 0;
|
||||
lt.tm_min = 0; // all intervals zero out seconds + minutes
|
||||
if (interval != EarningsInterval::Hour) lt.tm_hour = 0; // Day/Month/Year
|
||||
if (interval == EarningsInterval::Month || interval == EarningsInterval::Year) lt.tm_mday = 1;
|
||||
if (interval == EarningsInterval::Year) lt.tm_mon = 0; // January
|
||||
lt.tm_isdst = -1; // let mktime resolve DST for the normalized time
|
||||
return (std::int64_t)std::mktime(<);
|
||||
}
|
||||
|
||||
struct EarningsResult {
|
||||
double total = 0.0; // wallet-wide earnings for the period (mode applied)
|
||||
std::map<std::string, double> perAddress; // per receiving (Inflows) / source-debited (Net) address
|
||||
int inflowCount = 0; // inflow txs counted in the period
|
||||
};
|
||||
|
||||
// Sum earnings over the current `interval` period. Inflows = received + mined (>= 0). Net also
|
||||
// subtracts sends (so the total can be negative). `nowSec` is passed in for testability.
|
||||
inline EarningsResult computeEarnings(const std::vector<TransactionInfo>& txs, std::int64_t nowSec,
|
||||
EarningsInterval interval, EarningsMode mode) {
|
||||
EarningsResult r;
|
||||
const std::int64_t start = earningsPeriodStart(nowSec, interval);
|
||||
const bool net = (mode == EarningsMode::Net);
|
||||
double totalIn = 0.0, totalOut = 0.0;
|
||||
for (const auto& tx : txs) {
|
||||
if (tx.timestamp < start) continue;
|
||||
if (tx.type == "receive" || tx.type == "mined") {
|
||||
const double a = std::fabs(tx.amount);
|
||||
totalIn += a;
|
||||
r.inflowCount++;
|
||||
if (!tx.address.empty()) r.perAddress[tx.address] += a;
|
||||
} else if (net && tx.type == "send") {
|
||||
const double a = std::fabs(tx.amount);
|
||||
totalOut += a;
|
||||
if (!tx.from_address.empty()) r.perAddress[tx.from_address] -= a;
|
||||
}
|
||||
}
|
||||
r.total = net ? (totalIn - totalOut) : totalIn;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Convenience: the earnings for a single address in a result (0 if the address earned nothing).
|
||||
inline double earningsForAddress(const EarningsResult& r, const std::string& address) {
|
||||
auto it = r.perAddress.find(address);
|
||||
return (it != r.perAddress.end()) ? it->second : 0.0;
|
||||
}
|
||||
|
||||
// Every input the result depends on: equal keys => the cached result is still valid. `periodStart` is
|
||||
// computed by the caller (earningsPeriodStart) so a period roll-over invalidates the cache.
|
||||
struct EarningsKey {
|
||||
std::uint64_t txRevision = 0; // WalletState::tx_revision (bumped by every write to transactions)
|
||||
std::size_t txCount = 0; // belt: a writer that skips the bump but changes the row count
|
||||
EarningsInterval interval = EarningsInterval::Day;
|
||||
EarningsMode mode = EarningsMode::Inflows;
|
||||
std::int64_t periodStart = 0;
|
||||
bool operator==(const EarningsKey& o) const {
|
||||
return txRevision == o.txRevision && txCount == o.txCount && interval == o.interval &&
|
||||
mode == o.mode && periodStart == o.periodStart;
|
||||
}
|
||||
};
|
||||
|
||||
// Revision-keyed cache (mirrors ui::MiningEarningsCache): the O(transactions) pass reruns only when an
|
||||
// input changes, plus a staleness safety net for a future writer that skips the revision bump.
|
||||
class EarningsCache {
|
||||
public:
|
||||
static constexpr std::int64_t kMaxAgeMs = 2000;
|
||||
const EarningsResult& get(const EarningsKey& key, const std::vector<TransactionInfo>& txs,
|
||||
std::int64_t nowSec, std::int64_t nowMs) {
|
||||
if (!valid_ || !(key_ == key) || (nowMs - computedAtMs_) > kMaxAgeMs) {
|
||||
result_ = computeEarnings(txs, nowSec, key.interval, key.mode);
|
||||
key_ = key;
|
||||
valid_ = true;
|
||||
computedAtMs_ = nowMs;
|
||||
computes_++;
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
int computeCount() const { return computes_; } // tests
|
||||
private:
|
||||
bool valid_ = false;
|
||||
EarningsKey key_;
|
||||
std::int64_t computedAtMs_ = 0;
|
||||
EarningsResult result_;
|
||||
int computes_ = 0;
|
||||
};
|
||||
|
||||
} // namespace data
|
||||
} // namespace dragonx
|
||||
@@ -106,6 +106,8 @@ struct SettingsPageState {
|
||||
float noise_opacity = 0.5f;
|
||||
float ui_opacity = 1.0f;
|
||||
float window_opacity = 1.0f;
|
||||
float favorite_opacity = 1.0f;
|
||||
int earnings_format = 1;
|
||||
std::string balance_layout = "classic";
|
||||
bool scanline_enabled = true;
|
||||
bool theme_effects_enabled = true;
|
||||
@@ -448,6 +450,8 @@ static void loadSettingsPageState(App* app) {
|
||||
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
|
||||
s_settingsState.ui_opacity = settings->getUIOpacity();
|
||||
s_settingsState.window_opacity = settings->getWindowOpacity();
|
||||
s_settingsState.favorite_opacity = settings->getFavoriteOpacity();
|
||||
s_settingsState.earnings_format = settings->getEarningsFormat();
|
||||
s_settingsState.noise_opacity = settings->getNoiseOpacity();
|
||||
|
||||
s_settingsState.gradient_background = settings->getGradientBackground();
|
||||
@@ -532,6 +536,8 @@ static void saveSettingsPageState(config::Settings* settings) {
|
||||
settings->setBlurMultiplier(blur);
|
||||
settings->setUIOpacity(lowSpecOverrides ? realPrefs.ui_opacity : s_settingsState.ui_opacity);
|
||||
settings->setWindowOpacity(s_settingsState.window_opacity); // 1.0 under low-spec, as the hotkey saves it
|
||||
settings->setFavoriteOpacity(s_settingsState.favorite_opacity); // not low-spec gated — cosmetic
|
||||
settings->setEarningsFormat(s_settingsState.earnings_format);
|
||||
settings->setNoiseOpacity(s_settingsState.noise_opacity);
|
||||
settings->setGradientBackground(s_settingsState.gradient_background);
|
||||
settings->setScanlineEnabled(lowSpecOverrides ? realPrefs.scanline_enabled : s_settingsState.scanline_enabled);
|
||||
@@ -1025,6 +1031,49 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
|
||||
// --- Favorite highlight opacity (gold outline + star button in the Balance address list) ---
|
||||
// Pure color-alpha preference; not low-spec gated (unlike the transparency sliders below).
|
||||
{
|
||||
ImGui::PushFont(body2);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(OnSurfaceMedium()));
|
||||
ImGui::TextUnformatted(TR("favorite_opacity"));
|
||||
ImGui::PopStyleColor();
|
||||
float favSliderW = std::min(std::max(S.drawElement("components.settings-page", "effects-input-min-width").size, contentW), 360.0f * dp);
|
||||
ImGui::SetNextItemWidth(favSliderW);
|
||||
{
|
||||
char favop_fmt[16];
|
||||
snprintf(favop_fmt, sizeof(favop_fmt), "%.0f%%%%", s_settingsState.favorite_opacity * 100.0f);
|
||||
if (ImGui::SliderFloat("##FavoriteOpacity", &s_settingsState.favorite_opacity, 0.15f, 1.0f, favop_fmt,
|
||||
ImGuiSliderFlags_AlwaysClamp)) {
|
||||
if (app->settings()) app->settings()->setFavoriteOpacity(s_settingsState.favorite_opacity); // live; persisted on release
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) saveSettingsPageState(app->settings());
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_favorite_opacity"));
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
|
||||
// --- Earnings display format (Balance tab per-address + total earnings figures) ---
|
||||
{
|
||||
ImGui::PushFont(body2);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(OnSurfaceMedium()));
|
||||
ImGui::TextUnformatted(TR("earnings_format"));
|
||||
ImGui::PopStyleColor();
|
||||
float efW = std::min(std::max(S.drawElement("components.settings-page", "effects-input-min-width").size, contentW), 360.0f * dp);
|
||||
ImGui::SetNextItemWidth(efW);
|
||||
const char* efItems[] = { TR("earnings_format_coin"), TR("earnings_format_coin_fiat"), TR("earnings_format_fiat") };
|
||||
if (ImGui::Combo("##EarningsFormat", &s_settingsState.earnings_format, efItems, IM_ARRAYSIZE(efItems))) {
|
||||
s_settingsState.earnings_format = std::max(0, std::min(2, s_settingsState.earnings_format));
|
||||
if (app->settings()) { app->settings()->setEarningsFormat(s_settingsState.earnings_format); app->settings()->save(); }
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_earnings_format"));
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
|
||||
// --- Effect toggles (always visible, horizontal wrapping flow) ---
|
||||
{
|
||||
ImGui::PushFont(body2);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "address_transfer_dialog.h"
|
||||
#include "mining_tab_helpers.h"
|
||||
#include "send_tab.h"
|
||||
#include "transactions_tab.h"
|
||||
|
||||
#include "../../app.h"
|
||||
#include "../../config/settings.h"
|
||||
@@ -35,6 +36,8 @@
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include "../../data/earnings_series.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -55,6 +58,44 @@ void UpdateBalanceLerp(App* app) {
|
||||
StepDisplayLerp(s_dispUnconfirmed, state.unconfirmed_balance, dt, kBalanceLerpSpeed, instant);
|
||||
}
|
||||
|
||||
// Total period-earnings for the whole wallet, formatted per the earnings settings. Only shown when the
|
||||
// global earnings toggle is on, so the default hero/layout appearance is unchanged. Own revision-keyed
|
||||
// cache (independent of the per-row one in RenderSharedAddressList).
|
||||
bool FormatTotalEarnings(App* app, std::string& outText, ImU32& outCol) {
|
||||
using namespace material;
|
||||
if (!app->settings() || !app->settings()->getEarningsShowAll()) return false;
|
||||
auto& S = schema::UI();
|
||||
const auto& state = app->state();
|
||||
auto* est = app->settings();
|
||||
static dragonx::data::EarningsCache s_totalEarnCache;
|
||||
auto iv = (dragonx::data::EarningsInterval)est->getEarningsInterval();
|
||||
auto md = (dragonx::data::EarningsMode)est->getEarningsMode();
|
||||
const int fmt = est->getEarningsFormat();
|
||||
const std::int64_t nowSec = (std::int64_t)std::time(nullptr);
|
||||
const std::int64_t nowMs = (std::int64_t)std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
dragonx::data::EarningsKey ek;
|
||||
ek.txRevision = state.tx_revision;
|
||||
ek.txCount = state.transactions.size();
|
||||
ek.interval = iv;
|
||||
ek.mode = md;
|
||||
ek.periodStart = dragonx::data::earningsPeriodStart(nowSec, iv);
|
||||
const auto& r = s_totalEarnCache.get(ek, state.transactions, nowSec, nowMs);
|
||||
const double ev = r.total;
|
||||
const double fiat = ev * state.market.price_usd;
|
||||
const bool haveFiat = state.market.price_usd > 0.0;
|
||||
const int f = haveFiat ? fmt : 0;
|
||||
char buf[64];
|
||||
if (f == 0) snprintf(buf, sizeof(buf), "%+.4f", ev);
|
||||
else if (f == 2) snprintf(buf, sizeof(buf), "%s$%.2f", ev < 0 ? "-" : "+", std::fabs(fiat));
|
||||
else snprintf(buf, sizeof(buf), "%+.4f ($%.2f)", ev, fiat);
|
||||
outText = buf;
|
||||
outCol = ev > 1e-12 ? S.resolveColor("var(--accent-shielded)", Success())
|
||||
: (ev < -1e-12 ? S.resolveColor("var(--accent-error)", ReadableError())
|
||||
: OnSurfaceDisabled());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Render compact hero line: logo + balance + USD + mining on one line
|
||||
void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float vs, float heroHeightOverride) {
|
||||
using namespace material;
|
||||
@@ -95,6 +136,18 @@ void RenderCompactHero(App* app, ImDrawList* dl, float availW, float hs, float v
|
||||
ImGui::Dummy(ImVec2(capFont->CalcTextSizeA(tickerFontSize, 10000, 0, DRAGONX_TICKER).x + Layout::spacingLg(), lineH));
|
||||
ImGui::SameLine();
|
||||
|
||||
// Total period-earnings (left of the USD) when the global earnings toggle is on.
|
||||
{
|
||||
std::string earnText; ImU32 earnCol;
|
||||
if (FormatTotalEarnings(app, earnText, earnCol)) {
|
||||
dl->AddText(capFont, tickerFontSize,
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, tickerY), earnCol, earnText.c_str());
|
||||
float ew = capFont->CalcTextSizeA(tickerFontSize, 10000, 0, earnText.c_str()).x;
|
||||
ImGui::Dummy(ImVec2(ew + Layout::spacingLg(), lineH));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
// USD value
|
||||
const auto& state = app->state();
|
||||
double usd_value = state.getBalanceUSD();
|
||||
@@ -163,6 +216,29 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
addRows(state.t_addresses, false);
|
||||
auto rows = BuildAddressListRows(rowInputs, addr_search, s_hideZeroBalances, s_showHidden);
|
||||
|
||||
// ---- Inline period-earnings: compute once (cached on tx_revision + interval/mode/period) ----
|
||||
static dragonx::data::EarningsCache s_earnCache;
|
||||
const dragonx::data::EarningsResult* earnResult = nullptr;
|
||||
bool earnShowAll = false;
|
||||
int earnFormat = 1; // 0=coin 1=coin+fiat 2=fiat
|
||||
if (app->settings()) {
|
||||
auto* est = app->settings();
|
||||
earnShowAll = est->getEarningsShowAll();
|
||||
earnFormat = est->getEarningsFormat();
|
||||
auto iv = (dragonx::data::EarningsInterval)est->getEarningsInterval();
|
||||
auto md = (dragonx::data::EarningsMode)est->getEarningsMode();
|
||||
const std::int64_t nowSec = (std::int64_t)std::time(nullptr);
|
||||
const std::int64_t nowMs = (std::int64_t)std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
dragonx::data::EarningsKey ek;
|
||||
ek.txRevision = state.tx_revision;
|
||||
ek.txCount = state.transactions.size();
|
||||
ek.interval = iv;
|
||||
ek.mode = md;
|
||||
ek.periodStart = dragonx::data::earningsPeriodStart(nowSec, iv);
|
||||
earnResult = &s_earnCache.get(ek, state.transactions, nowSec, nowMs);
|
||||
}
|
||||
|
||||
// ---- Toolbar: search + checkboxes + create buttons ----
|
||||
float avail = ImGui::GetContentRegionAvail().x;
|
||||
float schemaMaxW = (searchIn.maxWidth >= 0) ? searchIn.maxWidth : 250.0f;
|
||||
@@ -184,39 +260,46 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
}
|
||||
}
|
||||
|
||||
float buttonWidth = ((addrBtn.width > 0) ? addrBtn.width : 140.0f) * dp;
|
||||
float spacing = ((addrBtn.gap > 0) ? addrBtn.gap : 8.0f) * dp;
|
||||
float totalButtonsWidth = buttonWidth * 2 + spacing;
|
||||
float kMinButtonsPosition = std::max(S.drawElement("tabs.balance", "min-buttons-position").size,
|
||||
S.drawElement("tabs.balance", "buttons-position").size * hs);
|
||||
ImGui::SameLine(std::max(kMinButtonsPosition,
|
||||
ImGui::GetWindowWidth() - totalButtonsWidth -
|
||||
S.drawElement("tabs.balance", "button-row-right-margin").sizeOr(16.0f)));
|
||||
// ---- Inline period-earnings controls: interval pills (1H/1D/1M/1Y), mode toggle (received /
|
||||
// net), and a global show-all toggle. The create-address buttons moved below the list (footer). ----
|
||||
if (app->settings()) {
|
||||
auto* est = app->settings();
|
||||
ImFont* icoF = Type().iconSmall();
|
||||
const float segH = ImGui::GetFrameHeight(); // fixed height so pills + icon buttons align
|
||||
// A compact selected/unselected toggle. `font` nullptr = default font (text pills).
|
||||
auto segBtn = [&](const char* idlabel, bool selected, const char* tip, ImFont* font) -> bool {
|
||||
if (font) ImGui::PushFont(font);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, selected ? WithAlpha(Primary(), 70) : IM_COL32(255, 255, 255, 12));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, WithAlpha(Primary(), 110));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, WithAlpha(Primary(), 140));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(selected ? OnSurface() : OnSurfaceMedium()));
|
||||
bool clk = ImGui::Button(idlabel, ImVec2(0, segH));
|
||||
ImGui::PopStyleColor(4);
|
||||
if (font) ImGui::PopFont();
|
||||
if (tip && ImGui::IsItemHovered()) material::Tooltip("%s", tip);
|
||||
return clk;
|
||||
};
|
||||
|
||||
bool sharedAddrSyncing = state.sync.syncing && !state.sync.isSynced();
|
||||
ImGui::BeginDisabled(sharedAddrSyncing);
|
||||
if (s_generating_z_address) {
|
||||
char genLabel[64];
|
||||
snprintf(genLabel, sizeof(genLabel), "%s%s##shared_z", TR("generating"), material::LoadingDots());
|
||||
TactileButton(genLabel, ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font));
|
||||
} else if (TactileButton(TR("new_z_address"), ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
|
||||
s_generating_z_address = true;
|
||||
app->createNewZAddress([](const std::string& addr) {
|
||||
s_generating_z_address = false;
|
||||
DEBUG_LOGF("Created new z-address: %s\n", addr.c_str());
|
||||
});
|
||||
ImGui::SameLine(0, Layout::spacingLg());
|
||||
// Interval pills
|
||||
const char* ivText[4] = { "1H", "1D", "1M", "1Y" };
|
||||
int iv = est->getEarningsInterval();
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
char lbl[16]; snprintf(lbl, sizeof(lbl), "%s##earniv%d", ivText[k], k);
|
||||
if (segBtn(lbl, iv == k, TR("tt_earn_interval"), nullptr)) { est->setEarningsInterval(k); est->save(); }
|
||||
if (k < 3) ImGui::SameLine(0, 2.0f * dp);
|
||||
}
|
||||
// Mode toggle (received-only vs net)
|
||||
ImGui::SameLine(0, Layout::spacingMd());
|
||||
int emode = est->getEarningsMode();
|
||||
if (segBtn(ICON_MD_CALL_RECEIVED "##earnmodein", emode == 0, TR("tt_earn_mode_inflows"), icoF)) { est->setEarningsMode(0); est->save(); }
|
||||
ImGui::SameLine(0, 2.0f * dp);
|
||||
if (segBtn(ICON_MD_SWAP_VERT "##earnmodenet", emode == 1, TR("tt_earn_mode_net"), icoF)) { est->setEarningsMode(1); est->save(); }
|
||||
// Global earnings-column toggle
|
||||
ImGui::SameLine(0, Layout::spacingMd());
|
||||
bool showAll = est->getEarningsShowAll();
|
||||
if (segBtn(ICON_MD_TRENDING_UP "##earnshowall", showAll, TR("tt_earn_show_all"), icoF)) { est->setEarningsShowAll(!showAll); est->save(); }
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("new_mining_address"), ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
|
||||
app->createNewTAddress([](const std::string& addr) {
|
||||
DEBUG_LOGF("Created new t-address: %s\n", addr.c_str());
|
||||
});
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_new_mining_address"));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
@@ -278,6 +361,7 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
addrScrollMaxY = ImGui::GetScrollMaxY();
|
||||
ImU32 greenCol = S.resolveColor("var(--accent-shielded)", Success());
|
||||
ImU32 goldCol = S.resolveColor("var(--accent-transparent)", Warning());
|
||||
ImU32 earnNegCol = S.resolveColor("var(--accent-error)", material::ReadableError()); // negative net earnings
|
||||
float rowPadLeft = Layout::cardInnerPadding();
|
||||
float rowPadRight = Layout::cardInnerPadding();
|
||||
float rowIconSz = std::max(S.drawElement("tabs.balance", "address-icon-min-size").size,
|
||||
@@ -285,10 +369,13 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
float innerW = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
// Theme colors for buttons (resolved once)
|
||||
ImU32 favGoldFill = S.resolveColor("var(--favorite-fill)", IM_COL32(255, 200, 50, 40));
|
||||
ImU32 favGoldBorder = S.resolveColor("var(--favorite-border)", IM_COL32(255, 200, 50, 100));
|
||||
ImU32 favGoldIcon = S.resolveColor("var(--favorite-icon)", IM_COL32(255, 200, 50, 255));
|
||||
ImU32 favGoldOutline= S.resolveColor("var(--favorite-outline)", IM_COL32(255, 200, 50, 120));
|
||||
// User-adjustable opacity for the favorite gold visuals (Settings > Appearance). 1.0 =
|
||||
// theme default (unchanged); scales whatever alpha the skin resolved, so it is skin-agnostic.
|
||||
const float favOp = app->settings() ? app->settings()->getFavoriteOpacity() : 1.0f;
|
||||
ImU32 favGoldFill = ScaleAlpha(S.resolveColor("var(--favorite-fill)", IM_COL32(255, 200, 50, 40)), favOp);
|
||||
ImU32 favGoldBorder = ScaleAlpha(S.resolveColor("var(--favorite-border)", IM_COL32(255, 200, 50, 100)), favOp);
|
||||
ImU32 favGoldIcon = ScaleAlpha(S.resolveColor("var(--favorite-icon)", IM_COL32(255, 200, 50, 255)), favOp);
|
||||
ImU32 favGoldOutline= ScaleAlpha(S.resolveColor("var(--favorite-outline)", IM_COL32(255, 200, 50, 120)), favOp);
|
||||
ImU32 btnFill = S.resolveColor("var(--row-button-fill)", IM_COL32(255, 255, 255, 12));
|
||||
ImU32 btnFillHov = S.resolveColor("var(--row-button-fill-hover)", IM_COL32(255, 255, 255, 25));
|
||||
ImU32 btnBorder = S.resolveColor("var(--row-button-border)", IM_COL32(255, 255, 255, 25));
|
||||
@@ -590,9 +677,33 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Line-2 right content widths: USD + optional period-earnings to its left ----
|
||||
std::string usdText = FormatAddressUsdValue(addr.balance, state.market.price_usd);
|
||||
float usdW = usdText.empty() ? 0.0f
|
||||
: capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, usdText.c_str()).x;
|
||||
const bool haveFiat = state.market.price_usd > 0.0;
|
||||
bool earnShown = earnResult && app->settings() &&
|
||||
(earnShowAll || app->settings()->isAddressEarningsShown(addr.address));
|
||||
char earnBuf[48]; earnBuf[0] = '\0';
|
||||
float earnW = 0.0f; ImU32 earnCol = OnSurfaceDisabled();
|
||||
if (earnShown) {
|
||||
double ev = dragonx::data::earningsForAddress(*earnResult, addr.address);
|
||||
double fiat = ev * state.market.price_usd;
|
||||
int fmt = (!haveFiat) ? 0 : earnFormat; // no price -> coin only
|
||||
if (fmt == 0) snprintf(earnBuf, sizeof(earnBuf), "%+.4f", ev);
|
||||
else if (fmt == 2) snprintf(earnBuf, sizeof(earnBuf), "%s$%.2f", ev < 0 ? "-" : "+", std::fabs(fiat));
|
||||
else snprintf(earnBuf, sizeof(earnBuf), "%+.4f ($%.2f)", ev, fiat);
|
||||
earnW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, earnBuf).x;
|
||||
earnCol = ev > 1e-12 ? greenCol : (ev < -1e-12 ? earnNegCol : OnSurfaceDisabled());
|
||||
if (row.hidden) earnCol = OnSurfaceDisabled();
|
||||
}
|
||||
// Left edge of the line-2 right content, so the address truncates before it.
|
||||
const float line2RightStart = contentRight - usdW
|
||||
- (earnShown && earnBuf[0] ? (earnW + Layout::spacingMd()) : 0.0f);
|
||||
|
||||
// ---- Address (second line) ----
|
||||
{
|
||||
float addrAvailW = contentRight - labelX;
|
||||
float addrAvailW = line2RightStart - Layout::spacingMd() - labelX;
|
||||
ImVec2 fullAddrSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0.0f, addr.address.c_str());
|
||||
std::string display_addr;
|
||||
if (fullAddrSz.x <= addrAvailW) {
|
||||
@@ -620,14 +731,17 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
DrawTextShadow(dl, body2, body2->LegacySize, ImVec2(balX, cy), balCol, balBuf);
|
||||
}
|
||||
|
||||
// ---- USD value (second line, right-aligned) ----
|
||||
// ---- Line 2 right: period-earnings (left of) + USD value, right-aligned ----
|
||||
{
|
||||
std::string usdText = FormatAddressUsdValue(addr.balance, state.market.price_usd);
|
||||
if (!usdText.empty()) {
|
||||
ImVec2 usdSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, usdText.c_str());
|
||||
const float line2Y = cy + body2->LegacySize + Layout::spacingXs();
|
||||
if (earnShown && earnBuf[0]) {
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(contentRight - usdSz.x,
|
||||
cy + body2->LegacySize + Layout::spacingXs()),
|
||||
ImVec2(contentRight - usdW - Layout::spacingMd() - earnW, line2Y),
|
||||
earnCol, earnBuf);
|
||||
}
|
||||
if (!usdText.empty()) {
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(contentRight - usdW, line2Y),
|
||||
OnSurfaceDisabled(), usdText.c_str());
|
||||
}
|
||||
}
|
||||
@@ -690,6 +804,10 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
SetSendFromAddress(addr.address);
|
||||
app->setCurrentPage(NavPage::Send);
|
||||
}
|
||||
if (ImGui::MenuItem(TR("view_transactions"))) {
|
||||
SetTransactionsAddressFilter(addr.address);
|
||||
app->setCurrentPage(NavPage::History);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem(TR("set_label"))) {
|
||||
AddressLabelDialog::show(app, addr.address, row.isZ);
|
||||
@@ -720,6 +838,15 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
if (ImGui::MenuItem(TR("favorite_address")))
|
||||
app->favoriteAddress(addr.address);
|
||||
}
|
||||
// Per-address earnings column toggle (when the global show-all toggle is off).
|
||||
if (app->settings()) {
|
||||
const bool shown = app->settings()->isAddressEarningsShown(addr.address);
|
||||
if (ImGui::MenuItem(shown ? TR("hide_earnings") : TR("show_earnings"))) {
|
||||
if (shown) app->settings()->hideAddressEarnings(addr.address);
|
||||
else app->settings()->showAddressEarnings(addr.address);
|
||||
app->settings()->save();
|
||||
}
|
||||
}
|
||||
// Portfolio membership — toggle this address in each Market-tab portfolio entry.
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu(TR("portfolio_add_to"))) {
|
||||
@@ -810,7 +937,8 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
listPanelMin.y, listPanelMax.y, fadeZone, addrScrollY, addrScrollMaxY);
|
||||
}
|
||||
|
||||
// Address count
|
||||
// Address count + create-address buttons (relocated below the list to free the toolbar row for
|
||||
// the earnings controls). Count on the left, New Z / New mining right-aligned on the same row.
|
||||
{
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
char countBuf[128];
|
||||
@@ -818,6 +946,38 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
snprintf(countBuf, sizeof(countBuf), TR("showing_x_of_y"),
|
||||
(int)rows.size(), totalAddrs);
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), countBuf);
|
||||
|
||||
float buttonWidth = ((addrBtn.width > 0) ? addrBtn.width : 140.0f) * dp;
|
||||
float spacing = ((addrBtn.gap > 0) ? addrBtn.gap : 8.0f) * dp;
|
||||
float totalButtonsWidth = buttonWidth * 2 + spacing;
|
||||
float rightMargin = S.drawElement("tabs.balance", "button-row-right-margin").sizeOr(16.0f);
|
||||
ImGui::SameLine(std::max(ImGui::GetCursorPosX(),
|
||||
ImGui::GetWindowWidth() - totalButtonsWidth - rightMargin));
|
||||
|
||||
bool sharedAddrSyncing = state.sync.syncing && !state.sync.isSynced();
|
||||
ImGui::BeginDisabled(sharedAddrSyncing);
|
||||
if (s_generating_z_address) {
|
||||
char genLabel[64];
|
||||
snprintf(genLabel, sizeof(genLabel), "%s%s##shared_z", TR("generating"), material::LoadingDots());
|
||||
TactileButton(genLabel, ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font));
|
||||
} else if (TactileButton(TR("new_z_address"), ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
|
||||
s_generating_z_address = true;
|
||||
app->createNewZAddress([](const std::string& addr) {
|
||||
s_generating_z_address = false;
|
||||
DEBUG_LOGF("Created new z-address: %s\n", addr.c_str());
|
||||
});
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("new_mining_address"), ImVec2(buttonWidth, 0),
|
||||
S.resolveFont(addrBtn.font.empty() ? "button" : addrBtn.font))) {
|
||||
app->createNewTAddress([](const std::string& addr) {
|
||||
DEBUG_LOGF("Created new t-address: %s\n", addr.c_str());
|
||||
});
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_new_mining_address"));
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
@@ -31,5 +33,10 @@ void RenderSharedAddressList(App* app, float listH, float availW, float glassRou
|
||||
void RenderSharedRecentTx(App* app, float recentH, float availW, float hs, float vs);
|
||||
void RenderSyncBar(App* app, ImDrawList* dl, float vs);
|
||||
|
||||
// Total period-earnings for the whole wallet, formatted per the earnings settings. Returns false
|
||||
// (and leaves out* untouched) when the global earnings toggle is off, so layouts show it only when
|
||||
// the user has opted into the earnings view. Shared by RenderCompactHero and the layout totals.
|
||||
bool FormatTotalEarnings(App* app, std::string& outText, ImU32& outCol);
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
|
||||
@@ -364,7 +364,7 @@ static void RenderBalanceClassic(App* app)
|
||||
OnSurfaceMedium(), DRAGONX_TICKER);
|
||||
cy += sub1->LegacySize + valGap;
|
||||
|
||||
// USD value
|
||||
// USD value (+ total period-earnings to its right when the earnings toggle is on)
|
||||
{
|
||||
double usd_value = state.getBalanceUSD();
|
||||
if (usd_value > 0.0)
|
||||
@@ -373,6 +373,12 @@ static void RenderBalanceClassic(App* app)
|
||||
snprintf(buf, sizeof(buf), "$-.-- USD");
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy),
|
||||
OnSurfaceDisabled(), buf);
|
||||
std::string earnText; ImU32 earnCol;
|
||||
if (FormatTotalEarnings(app, earnText, earnCol)) {
|
||||
float usdW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, buf).x;
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(cx + usdW + tickGap * 3.0f, cy), earnCol, earnText.c_str());
|
||||
}
|
||||
}
|
||||
cy += capFont->LegacySize + 2 * dp;
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "../../util/texture_loader.h"
|
||||
#include "../../util/platform.h"
|
||||
#include "image_picker.h"
|
||||
#include "transactions_tab.h"
|
||||
#include "../sidebar.h"
|
||||
#include "../layout.h"
|
||||
#include "imgui.h"
|
||||
#include <filesystem>
|
||||
@@ -530,6 +532,11 @@ void RenderContactsTab(App* app)
|
||||
s_show_edit_dialog = true;
|
||||
s_focus_edit_field = true;
|
||||
};
|
||||
auto doViewTransactions = [&]() {
|
||||
if (!selValid()) return;
|
||||
SetTransactionsAddressFilter(book.entries()[s_selected_index].address);
|
||||
app->setCurrentPage(ui::NavPage::History);
|
||||
};
|
||||
|
||||
// Add/edit form — a modal popup layered over the tab, with a live contact preview and an avatar
|
||||
// picker (default Z/T badge, a Material icon, or a custom image via the in-app image picker).
|
||||
@@ -1367,7 +1374,7 @@ void RenderContactsTab(App* app)
|
||||
// Label (line 1) + muted address (line 2) — reserve trailing room for the globe + actions.
|
||||
const float actHit = icoF->LegacySize + 8.0f * dp; // per-action square hit area
|
||||
const float globeW = entry.isGlobal() ? (icoF->LegacySize + 8.0f * dp) : 0.0f;
|
||||
const float trailW = 3.0f * actHit + 6.0f * dp + globeW;
|
||||
const float trailW = 4.0f * actHit + 8.0f * dp + globeW; // copy / view-tx / edit / delete
|
||||
const float cy = mn.y + rowH * 0.5f;
|
||||
const float tx = (s_contactsShape == 2) ? (mn.x + tabW + pad)
|
||||
: (mn.x + pad + avR * 2.0f + pad);
|
||||
@@ -1399,10 +1406,11 @@ void RenderContactsTab(App* app)
|
||||
rightX -= gs.x + 8.0f * dp;
|
||||
}
|
||||
if (rowHovered || selected) {
|
||||
const char* actGlyph[3] = { ICON_MD_CONTENT_COPY, ICON_MD_EDIT, ICON_MD_DELETE };
|
||||
const char* actTip[3] = { TR("copy_address"), TR("edit"), TR("delete") };
|
||||
// Order (left->right): copy, view-transactions, edit, delete. Drawn right-to-left.
|
||||
const char* actGlyph[4] = { ICON_MD_CONTENT_COPY, ICON_MD_RECEIPT_LONG, ICON_MD_EDIT, ICON_MD_DELETE };
|
||||
const char* actTip[4] = { TR("copy_address"), TR("view_transactions"), TR("edit"), TR("delete") };
|
||||
float ax = rightX - actHit; // rightmost action (delete), just left of the globe
|
||||
for (int a = 2; a >= 0; --a) {
|
||||
for (int a = 3; a >= 0; --a) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(ax, cy - actHit * 0.5f));
|
||||
ImGui::PushID(a + 100);
|
||||
const bool aClk = ImGui::InvisibleButton("##act", ImVec2(actHit, actHit));
|
||||
@@ -1411,12 +1419,13 @@ void RenderContactsTab(App* app)
|
||||
if (aClk) {
|
||||
s_selected_index = static_cast<int>(i);
|
||||
if (a == 0) doCopy();
|
||||
else if (a == 1) openEdit();
|
||||
else if (a == 1) doViewTransactions();
|
||||
else if (a == 2) openEdit();
|
||||
// Delete mutates book.entries() (removeEntry -> erase) — deferring it past the
|
||||
// row loop avoids iterating a vector we shrink mid-loop (OOB on later rows).
|
||||
else rowDeleteRequested = true;
|
||||
}
|
||||
const bool armedDel = (a == 2 && s_confirm_delete_idx == static_cast<int>(i));
|
||||
const bool armedDel = (a == 3 && s_confirm_delete_idx == static_cast<int>(i));
|
||||
const ImU32 acol = armedDel ? material::ReadableError()
|
||||
: aHov ? material::OnSurface()
|
||||
: material::OnSurfaceMedium();
|
||||
@@ -1454,10 +1463,11 @@ void RenderContactsTab(App* app)
|
||||
// Shared right-click context menu (opened by either view's row right-click; acts on the selection).
|
||||
if (openContextMenu) ImGui::OpenPopup("##contactCtx");
|
||||
if (ImGui::BeginPopup("##contactCtx")) {
|
||||
if (ImGui::MenuItem(TR("copy_address"))) doCopy();
|
||||
if (ImGui::MenuItem(TR("edit"))) openEdit();
|
||||
if (ImGui::MenuItem(TR("copy_address"))) doCopy();
|
||||
if (ImGui::MenuItem(TR("view_transactions"))) doViewTransactions();
|
||||
if (ImGui::MenuItem(TR("edit"))) openEdit();
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem(TR("delete"))) doDelete();
|
||||
if (ImGui::MenuItem(TR("delete"))) doDelete();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "../../util/i18n.h"
|
||||
#include "../../util/platform.h"
|
||||
#include "../../config/settings.h"
|
||||
#include "../../util/pool_registry.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../material/type.h"
|
||||
#include "../material/draw_helpers.h"
|
||||
@@ -68,15 +69,38 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
minedAllTimeCount = earn.allTimeCount;
|
||||
const std::vector<MiningEarningsRecentTx>& recentMined = earn.recent;
|
||||
|
||||
// Est. Daily = expected reward for YOUR own hashrate share of the network. In pool mode this uses
|
||||
// your local miner rate (pool_mining.hashrate_10s), NOT the pool's aggregate rate: pool payouts are
|
||||
// share-proportional, so your expected daily is the same solo-equivalent value (a rough estimate,
|
||||
// before the pool fee). Using the pool's total rate here would show the POOL's earnings, not yours. (M-05)
|
||||
double estHashrate = s_pool_mode ? state.pool_mining.hashrate_10s : mining.localHashrate;
|
||||
// Est. Daily is a statistical long-run AVERAGE of your reward for your hashrate share of the
|
||||
// network -- NOT a steady income. Actual rewards arrive at random (Poisson): often nothing for
|
||||
// long stretches, then a lump. We feed EstimateHoursToBlock a TRAILING-AVERAGE hashrate (not an
|
||||
// instantaneous / 10s snapshot) so a spike or a momentary dip doesn't swing the figure, and in
|
||||
// pool mode we subtract the known pool fee so the number is net. Pool payouts are share-
|
||||
// proportional, so your expected daily equals this solo-equivalent value (using the pool's
|
||||
// aggregate rate would show the POOL's earnings, not yours). (M-05 / A+B)
|
||||
double estHashrate;
|
||||
if (s_pool_mode) {
|
||||
const auto& pm = state.pool_mining;
|
||||
estHashrate = pm.hashrate_15m > 0.0 ? pm.hashrate_15m
|
||||
: pm.hashrate_60s > 0.0 ? pm.hashrate_60s
|
||||
: pm.hashrate_10s; // 15m avg preferred; fall back as it warms up
|
||||
} else {
|
||||
estHashrate = mining.localHashrate;
|
||||
if (mining.hashrate_history.size() >= 10) { // enough samples -> trailing mean
|
||||
double sum = 0.0;
|
||||
for (double h : mining.hashrate_history) sum += h;
|
||||
estHashrate = sum / (double)mining.hashrate_history.size();
|
||||
}
|
||||
}
|
||||
double est_hours_2 = EstimateHoursToBlock(estHashrate, mining.networkHashrate, mining.difficulty);
|
||||
double estDailyBlocks = (est_hours_2 > 0) ? (24.0 / est_hours_2) : 0.0;
|
||||
double blockReward = schema::UI().drawElement("business", "block-reward").size;
|
||||
double estDaily = estDailyBlocks * blockReward;
|
||||
// Pool fee: compile-time known fee for the configured pool (<0 = unknown/synthetic pool -> gross).
|
||||
double estFeePct = -1.0;
|
||||
if (s_pool_mode) {
|
||||
const util::KnownPool* kp = util::findKnownPoolByUrl(state.pool_mining.pool_url);
|
||||
if (kp && kp->feePercent >= 0.0) estFeePct = kp->feePercent;
|
||||
}
|
||||
if (s_pool_mode && estFeePct > 0.0) estDaily *= (1.0 - estFeePct / 100.0);
|
||||
bool estActive = isMiningActive && estDaily > 0;
|
||||
|
||||
ImU32 greenCol2 = Success();
|
||||
@@ -185,15 +209,19 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
snprintf(estVal, sizeof(estVal), "~%.4f", estDaily);
|
||||
else
|
||||
snprintf(estVal, sizeof(estVal), "%s", TR("grpc_na"));
|
||||
// Disclose in pool mode that Est. Daily is a rough solo-equivalent (before the pool fee), so the
|
||||
// number isn't silently mismatched to its plain "Est. Daily" label. (M-05)
|
||||
const char* estSub = (s_pool_mode && estActive) ? TR("mining_est_daily_pool_sub") : nullptr;
|
||||
// Flag that Est. Daily is a long-run AVERAGE in both modes (so it doesn't read as realized
|
||||
// income), and whether it is net of the pool fee. Tooltip below carries the full variance note.
|
||||
const char* estSub = !estActive ? nullptr
|
||||
: (s_pool_mode && estFeePct > 0.0) ? TR("mining_est_daily_sub_net")
|
||||
: TR("mining_est_daily_sub");
|
||||
// Neutral (not green/"income") colour for the estimate — it's a probability, not money earned.
|
||||
const ImU32 estCol = estActive ? OnSurfaceMedium() : OnSurfaceDisabled();
|
||||
|
||||
EarningsEntry entries[] = {
|
||||
{ TR("mining_today"), todayVal, todaySub, greenCol2 },
|
||||
{ TR("mining_yesterday"), yesterdayVal, yesterdaySub, OnSurface() },
|
||||
{ TR("mining_all_time"), allVal, allSub, OnSurface() },
|
||||
{ TR("mining_est_daily"), estVal, estSub, estActive ? greenCol2 : OnSurfaceDisabled() },
|
||||
{ TR("mining_est_daily"), estVal, estSub, estCol },
|
||||
};
|
||||
|
||||
for (int ei = 0; ei < numCols; ei++) {
|
||||
@@ -223,6 +251,27 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
ImVec2(centerX - valSz.x * 0.5f, valY), entries[ei].col, entries[ei].value);
|
||||
}
|
||||
}
|
||||
|
||||
// Hover tooltip over the Est. Daily column (last column): explain the Poisson variance and,
|
||||
// in pool mode, whether the figure is net of the fee.
|
||||
{
|
||||
float estColX = cardMin.x + pad + (numCols - 1) * colW;
|
||||
ImVec2 estMin(estColX, ey);
|
||||
ImVec2 estMax(estColX + colW, cardMin.y + earningsRowH);
|
||||
if (material::IsRectHovered(estMin, estMax)) {
|
||||
char tip[512];
|
||||
if (s_pool_mode && estFeePct > 0.0) {
|
||||
char feeNote[160];
|
||||
snprintf(feeNote, sizeof(feeNote), TR("tt_mining_est_daily_fee_fmt"), estFeePct);
|
||||
snprintf(tip, sizeof(tip), "%s\n%s", TR("tt_mining_est_daily"), feeNote);
|
||||
} else if (s_pool_mode && estFeePct < 0.0) { // fee unknown (synthetic/unmatched pool)
|
||||
snprintf(tip, sizeof(tip), "%s\n%s", TR("tt_mining_est_daily"), TR("tt_mining_est_daily_nofee"));
|
||||
} else { // solo, or a known 0%-fee pool (net == gross)
|
||||
snprintf(tip, sizeof(tip), "%s", TR("tt_mining_est_daily"));
|
||||
}
|
||||
material::Tooltip("%s", tip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Separator between earnings & details ===
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "../../wallet/lite_wallet_controller.h"
|
||||
#include "../../ui/widgets/qr_code.h"
|
||||
#include "../sidebar.h"
|
||||
#include "transactions_tab.h"
|
||||
#include "../layout.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../material/type.h"
|
||||
@@ -535,6 +536,7 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
|
||||
S.drawElement("tabs.balance", "row-hover-rounding").sizeOr(4.0f));
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(0)) {
|
||||
if (!tx.address.empty()) SetTransactionsAddressFilter(tx.address);
|
||||
app->setCurrentPage(ui::NavPage::History);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,17 @@ static bool containsIgnoreCase(const std::string& str, const std::string& search
|
||||
return dragonx::util::containsIgnoreCase(str, search);
|
||||
}
|
||||
|
||||
// Pending "view transactions for this address" request. Set by SetTransactionsAddressFilter()
|
||||
// from Contacts / the Balance address list / the Receive tab, consumed on the next
|
||||
// RenderTransactionsTab frame to pre-fill the search box (see the consume block below).
|
||||
static std::string s_pending_addr_filter;
|
||||
static bool s_pending_addr_filter_set = false;
|
||||
|
||||
void SetTransactionsAddressFilter(const std::string& address) {
|
||||
s_pending_addr_filter = address;
|
||||
s_pending_addr_filter_set = true;
|
||||
}
|
||||
|
||||
// A display-ready transaction that may be a merged autoshield pair.
|
||||
// For non-merged entries, send_idx or recv_idx is -1.
|
||||
struct DisplayTx {
|
||||
@@ -313,6 +324,15 @@ void RenderTransactionsTab(App* app)
|
||||
// Search & Filter bar
|
||||
// ================================================================
|
||||
static char search_filter[128] = "";
|
||||
// Consume a pending "view transactions for this address" request: pre-fill the search box
|
||||
// with the address and reset the type filter to All so no matching row is hidden. The
|
||||
// changed search_str resets pagination/expansion via filterInputsChanged() below.
|
||||
if (s_pending_addr_filter_set) {
|
||||
strncpy(search_filter, s_pending_addr_filter.c_str(), sizeof(search_filter) - 1);
|
||||
search_filter[sizeof(search_filter) - 1] = '\0';
|
||||
type_filter = 0;
|
||||
s_pending_addr_filter_set = false;
|
||||
}
|
||||
float searchMaxW = (searchInput.maxWidth >= 0) ? searchInput.maxWidth : 300.0f;
|
||||
float searchRatio = (searchInput.widthRatio >= 0) ? searchInput.widthRatio : 0.30f;
|
||||
float searchWidth = std::min(searchMaxW * hs, availWidth * searchRatio);
|
||||
@@ -530,6 +550,7 @@ void RenderTransactionsTab(App* app)
|
||||
}
|
||||
if (!search_str.empty()) {
|
||||
if (!containsIgnoreCase(dtx.address, search_str) &&
|
||||
!containsIgnoreCase(dtx.from_address, search_str) &&
|
||||
!containsIgnoreCase(dtx.txid, search_str) &&
|
||||
!containsIgnoreCase(dtx.memo, search_str) &&
|
||||
!(dtx.is_shield && containsIgnoreCase(std::string("shielded"), search_str))) {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dragonx {
|
||||
class App;
|
||||
|
||||
@@ -15,5 +17,16 @@ namespace ui {
|
||||
*/
|
||||
void RenderTransactionsTab(App* app);
|
||||
|
||||
/**
|
||||
* @brief Pre-filter the Transactions (History) tab by an address.
|
||||
*
|
||||
* Sets the History search box to @p address and resets the type filter to All on the next
|
||||
* render, then the caller navigates to the History tab. Used by the "View transactions"
|
||||
* actions in Contacts, the Balance address list, and the Receive tab. Because shielded /
|
||||
* received-from attribution is incomplete, this shows transactions *involving* the address,
|
||||
* not a guaranteed-complete set.
|
||||
*/
|
||||
void SetTransactionsAddressFilter(const std::string& address);
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
|
||||
@@ -520,6 +520,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["noise"] = "Noise";
|
||||
strings_["ui_opacity"] = "UI Opacity";
|
||||
strings_["window_opacity"] = "Window Opacity";
|
||||
strings_["favorite_opacity"] = "Favorite Highlight Opacity";
|
||||
strings_["font_scale"] = "Font Scale";
|
||||
strings_["refresh"] = "Refresh";
|
||||
strings_["website"] = "Website";
|
||||
@@ -795,6 +796,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["tt_noise"] = "Grain texture intensity (0% = off, 100% = maximum)";
|
||||
strings_["tt_ui_opacity"] = "Card and sidebar opacity (100% = fully opaque, lower = more see-through)";
|
||||
strings_["tt_window_opacity"] = "Background opacity (lower = desktop visible through window)";
|
||||
strings_["tt_favorite_opacity"] = "Opacity of the favorite star button and its gold outline in the address list (minimum 15%)";
|
||||
strings_["tt_font_scale"] = "Scale all text and UI (1.0x = default, up to 1.5x). Hotkey: Alt + Scroll Wheel";
|
||||
strings_["tt_custom_theme"] = "Custom theme active";
|
||||
strings_["tt_address_book"] = "Manage saved addresses for quick sending";
|
||||
@@ -1063,6 +1065,18 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["filter"] = "Filter...";
|
||||
strings_["no_addresses_yet"] = "No addresses yet";
|
||||
strings_["showing_x_of_y"] = "Showing %d of %d addresses";
|
||||
strings_["tt_earn_interval"] = "Earnings period: 1H = this hour, 1D = today, 1M = this month, 1Y = this year";
|
||||
strings_["tt_earn_mode_inflows"] = "Earnings mode: amounts received (including mined)";
|
||||
strings_["tt_earn_mode_net"] = "Earnings mode: net (received minus sent)";
|
||||
strings_["tt_earn_show_all"] = "Show the earnings figure for every address";
|
||||
strings_["show_earnings"] = "Show earnings";
|
||||
strings_["hide_earnings"] = "Hide earnings";
|
||||
strings_["earnings_label"] = "Earnings";
|
||||
strings_["earnings_format"] = "Earnings Display Format";
|
||||
strings_["tt_earnings_format"] = "How the per-address and total earnings figures are shown";
|
||||
strings_["earnings_format_coin"] = "Coin only";
|
||||
strings_["earnings_format_coin_fiat"] = "Coin + fiat";
|
||||
strings_["earnings_format_fiat"] = "Fiat only";
|
||||
strings_["set_label"] = "Set Label";
|
||||
strings_["copied"] = "Copied!";
|
||||
strings_["hidden_tag"] = " (hidden)";
|
||||
@@ -1193,6 +1207,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["mature"] = "Mature";
|
||||
strings_["copy_txid"] = "Copy TxID";
|
||||
strings_["view_details"] = "View Details";
|
||||
strings_["view_transactions"] = "View Transactions";
|
||||
strings_["tt_view_transactions"] = "Show transactions involving this address in the History tab";
|
||||
strings_["full_details"] = "Full Details";
|
||||
strings_["showing_transactions"] = "Showing %d\xe2\x80\x93%d of %d transactions (total: %zu)";
|
||||
strings_["txs_count"] = "%d txs";
|
||||
@@ -2180,6 +2196,11 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["mining_difficulty_copied"] = "Difficulty copied";
|
||||
strings_["mining_est_block"] = "Est. Block";
|
||||
strings_["mining_est_daily"] = "Est. Daily";
|
||||
strings_["mining_est_daily_sub"] = "avg";
|
||||
strings_["mining_est_daily_sub_net"] = "avg, net";
|
||||
strings_["tt_mining_est_daily"] = "A long-run statistical average for your current hashrate share of the network. Real mining rewards arrive at random (Poisson) - often nothing for long stretches, then a lump - so this is NOT a steady daily income.";
|
||||
strings_["tt_mining_est_daily_fee_fmt"] = "Shown after the %.1f%% pool fee.";
|
||||
strings_["tt_mining_est_daily_nofee"] = "Shown before the pool fee (fee unknown for this pool).";
|
||||
strings_["mining_filter_all"] = "All";
|
||||
strings_["mining_filter_tip_all"] = "Show all earnings";
|
||||
strings_["mining_filter_tip_pool"] = "Show pool earnings only";
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "util/secure_vault.h"
|
||||
#include "daemon/lifecycle_adapters.h"
|
||||
#include "data/wallet_state.h"
|
||||
#include "data/earnings_series.h"
|
||||
#include "rpc/connection.h"
|
||||
#include "rpc/rpc_failure.h"
|
||||
#include "rpc/rpc_busy_gate.h"
|
||||
@@ -18808,6 +18809,83 @@ void testReadFileExact()
|
||||
fs::remove_all(dir, ec);
|
||||
}
|
||||
|
||||
// ---- Period-earnings aggregation (data/earnings_series.h) ----
|
||||
void testEarningsSeries() {
|
||||
using namespace dragonx::data;
|
||||
using dragonx::TransactionInfo;
|
||||
|
||||
const std::int64_t now = 1700000000; // fixed epoch; periods derived from the function under test
|
||||
|
||||
auto mk = [](const char* type, double amt, std::int64_t ts, const char* addr, const char* from) {
|
||||
TransactionInfo t; t.type = type; t.amount = amt; t.timestamp = ts;
|
||||
t.address = addr; t.from_address = from; return t;
|
||||
};
|
||||
|
||||
// Day period: inflows vs net, per-address attribution, yesterday excluded.
|
||||
{
|
||||
const std::int64_t dayStart = earningsPeriodStart(now, EarningsInterval::Day);
|
||||
EXPECT_TRUE(dayStart <= now);
|
||||
EXPECT_TRUE(now - dayStart < 24 * 3600);
|
||||
|
||||
std::vector<TransactionInfo> txs;
|
||||
txs.push_back(mk("receive", 2.0, dayStart + 10, "zA", ""));
|
||||
txs.push_back(mk("mined", 1.0, dayStart + 20, "zMine", ""));
|
||||
txs.push_back(mk("send", -0.5, dayStart + 30, "zB", "zA")); // stored negative; counterparty in address
|
||||
txs.push_back(mk("receive", 99.0, dayStart - 10, "zA", "")); // yesterday: excluded
|
||||
|
||||
EarningsResult inflow = computeEarnings(txs, now, EarningsInterval::Day, EarningsMode::Inflows);
|
||||
EXPECT_NEAR(inflow.total, 3.0, 1e-9);
|
||||
EXPECT_EQ(inflow.inflowCount, 2);
|
||||
EXPECT_NEAR(earningsForAddress(inflow, "zA"), 2.0, 1e-9);
|
||||
EXPECT_NEAR(earningsForAddress(inflow, "zMine"), 1.0, 1e-9);
|
||||
EXPECT_NEAR(earningsForAddress(inflow, "zB"), 0.0, 1e-9); // sends not attributed in Inflows
|
||||
|
||||
EarningsResult net = computeEarnings(txs, now, EarningsInterval::Day, EarningsMode::Net);
|
||||
EXPECT_NEAR(net.total, 2.5, 1e-9); // 3 - 0.5
|
||||
EXPECT_NEAR(earningsForAddress(net, "zA"), 1.5, 1e-9); // +2 received - 0.5 sent-from
|
||||
EXPECT_NEAR(earningsForAddress(net, "zMine"), 1.0, 1e-9);
|
||||
}
|
||||
|
||||
// Hour period excludes a tx from the previous hour.
|
||||
{
|
||||
const std::int64_t hourStart = earningsPeriodStart(now, EarningsInterval::Hour);
|
||||
EXPECT_TRUE(now - hourStart < 3600);
|
||||
std::vector<TransactionInfo> txs;
|
||||
txs.push_back(mk("receive", 5.0, hourStart + 1, "z1", ""));
|
||||
txs.push_back(mk("receive", 7.0, hourStart - 1, "z1", ""));
|
||||
EarningsResult r = computeEarnings(txs, now, EarningsInterval::Hour, EarningsMode::Inflows);
|
||||
EXPECT_NEAR(r.total, 5.0, 1e-9);
|
||||
}
|
||||
|
||||
// Period starts nest: year <= month <= day <= hour <= now.
|
||||
{
|
||||
std::int64_t y = earningsPeriodStart(now, EarningsInterval::Year);
|
||||
std::int64_t m = earningsPeriodStart(now, EarningsInterval::Month);
|
||||
std::int64_t d = earningsPeriodStart(now, EarningsInterval::Day);
|
||||
std::int64_t h = earningsPeriodStart(now, EarningsInterval::Hour);
|
||||
EXPECT_TRUE(y <= m); EXPECT_TRUE(m <= d); EXPECT_TRUE(d <= h); EXPECT_TRUE(h <= now);
|
||||
}
|
||||
|
||||
// Cache recomputes only on key change / period roll-over (not on a within-window re-get).
|
||||
{
|
||||
std::vector<TransactionInfo> txs;
|
||||
txs.push_back(mk("receive", 1.0, now, "z1", ""));
|
||||
EarningsCache cache;
|
||||
EarningsKey k;
|
||||
k.txRevision = 1; k.txCount = txs.size(); k.interval = EarningsInterval::Day;
|
||||
k.mode = EarningsMode::Inflows; k.periodStart = earningsPeriodStart(now, EarningsInterval::Day);
|
||||
cache.get(k, txs, now, 1000);
|
||||
cache.get(k, txs, now, 1500); // same key, within kMaxAgeMs -> no recompute
|
||||
EXPECT_EQ(cache.computeCount(), 1);
|
||||
EarningsKey k2 = k; k2.txRevision = 2; // tx changed -> recompute
|
||||
cache.get(k2, txs, now, 1600);
|
||||
EXPECT_EQ(cache.computeCount(), 2);
|
||||
EarningsKey k3 = k2; k3.periodStart += 86400; // period rolled over -> recompute
|
||||
cache.get(k3, txs, now, 1700);
|
||||
EXPECT_EQ(cache.computeCount(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
testConnectionConfig();
|
||||
@@ -19134,6 +19212,7 @@ int main()
|
||||
testCaptureExtent();
|
||||
testResolveExchangeSelection();
|
||||
testTransactionCsv();
|
||||
testEarningsSeries();
|
||||
|
||||
if (g_failures != 0) {
|
||||
std::cerr << g_failures << " test assertion(s) failed\n";
|
||||
|
||||
Reference in New Issue
Block a user