From b07b84c3cbd45aaeb8f15c5df534c0d17cb13f05 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Mon, 20 Apr 2026 23:06:15 +0200 Subject: [PATCH] Refine peyawallet mining tab layout --- lib/l10n/app_en.arb | 1 + lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_en.dart | 4 + lib/l10n/app_localizations_pl.dart | 4 + lib/l10n/app_pl.arb | 1 + lib/ui/screens/mining_screen.dart | 586 ++++++++++++++--------------- 6 files changed, 294 insertions(+), 308 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7664c90..153d185 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -355,6 +355,7 @@ "miningSoloHint": "Solo mode will target the local daemon bundled with the wallet.", "miningSoloNeedsLocalNode": "Solo mode expects the wallet to use the local node.", "miningPoolHint": "Default pool is preconfigured for Peya and can be adjusted here.", + "miningPoolFixedHint": "Pool mode is fixed to the default Peya pool in this release. The endpoint is shown for visibility only.", "miningApiPortLabel": "XMRig API port", "miningResetDraftAction": "Reset draft", "miningConfigInvalid": "Enter a valid pool host and ports.", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 128ae37..413f62b 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1754,6 +1754,12 @@ abstract class AppLocalizations { /// **'Default pool is preconfigured for Peya and can be adjusted here.'** String get miningPoolHint; + /// No description provided for @miningPoolFixedHint. + /// + /// In en, this message translates to: + /// **'Pool mode is fixed to the default Peya pool in this release. The endpoint is shown for visibility only.'** + String get miningPoolFixedHint; + /// No description provided for @miningApiPortLabel. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index daf5b33..6b7749e 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -884,6 +884,10 @@ class AppLocalizationsEn extends AppLocalizations { String get miningPoolHint => 'Default pool is preconfigured for Peya and can be adjusted here.'; + @override + String get miningPoolFixedHint => + 'Pool mode is fixed to the default Peya pool in this release. The endpoint is shown for visibility only.'; + @override String get miningApiPortLabel => 'XMRig API port'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 9512fa1..ba84302 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -888,6 +888,10 @@ class AppLocalizationsPl extends AppLocalizations { String get miningPoolHint => 'Domyślny pool dla Peya jest już wpisany i można go tu zmienić.'; + @override + String get miningPoolFixedHint => + 'Tryb pool jest w tym wydaniu na stałe spięty z domyślnym poolem Peya. Endpoint pokazujemy tylko informacyjnie.'; + @override String get miningApiPortLabel => 'Port API XMRig'; diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index c2729a8..1a13ef7 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -355,6 +355,7 @@ "miningSoloHint": "Tryb solo będzie kierował kopanie do lokalnego daemona bundlowanego z walletem.", "miningSoloNeedsLocalNode": "Tryb solo zakłada, że portfel korzysta z lokalnego noda.", "miningPoolHint": "Domyślny pool dla Peya jest już wpisany i można go tu zmienić.", + "miningPoolFixedHint": "Tryb pool jest w tym wydaniu na stałe spięty z domyślnym poolem Peya. Endpoint pokazujemy tylko informacyjnie.", "miningApiPortLabel": "Port API XMRig", "miningResetDraftAction": "Przywróć szkic", "miningConfigInvalid": "Podaj poprawny host poola i porty.", diff --git a/lib/ui/screens/mining_screen.dart b/lib/ui/screens/mining_screen.dart index f96b366..32421b1 100644 --- a/lib/ui/screens/mining_screen.dart +++ b/lib/ui/screens/mining_screen.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; -import '../../app_features.dart'; import '../../domain/models.dart'; import '../../state/providers.dart'; import '../l10n/app_localizations_ext.dart'; @@ -20,8 +19,6 @@ class MiningScreen extends ConsumerStatefulWidget { class _MiningScreenState extends ConsumerState { late MiningMode _mode; late int _cpuThreads; - late final TextEditingController _poolHostController; - late final TextEditingController _poolPortController; late final TextEditingController _apiPortController; bool _dirty = false; @@ -31,15 +28,11 @@ class _MiningScreenState extends ConsumerState { final config = ref.read(appConfigControllerProvider).miningConfig; _mode = config.mode; _cpuThreads = config.cpuThreads; - _poolHostController = TextEditingController(text: config.poolHost); - _poolPortController = TextEditingController(text: config.poolPort.toString()); _apiPortController = TextEditingController(text: config.apiPort.toString()); } @override void dispose() { - _poolHostController.dispose(); - _poolPortController.dispose(); _apiPortController.dispose(); super.dispose(); } @@ -51,14 +44,6 @@ class _MiningScreenState extends ConsumerState { final config = ref.watch(appConfigControllerProvider); final walletState = ref.watch(walletControllerProvider); final maxThreads = math.max(1, math.min(Platform.numberOfProcessors, 64)); - final poolHost = _poolHostController.text.trim(); - final poolPort = int.tryParse(_poolPortController.text.trim()); - final apiPort = int.tryParse(_apiPortController.text.trim()); - final canSave = poolHost.isNotEmpty && - poolPort != null && - poolPort > 0 && - apiPort != null && - apiPort > 0; return SingleChildScrollView( padding: const EdgeInsets.all(24), @@ -82,295 +67,138 @@ class _MiningScreenState extends ConsumerState { ), ), const SizedBox(height: 24), - LayoutBuilder( - builder: (context, constraints) { - final wide = constraints.maxWidth >= 920; - return Flex( - direction: wide ? Axis.horizontal : Axis.vertical, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: wide ? 3 : 0, - child: Column( + Column( + children: [ + _SectionCard( + title: l10n.miningControlsTitle, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 12, + runSpacing: 12, children: [ - _SectionCard( - title: l10n.miningSourceTitle, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - DropdownButtonFormField( - value: _mode, - decoration: InputDecoration( - labelText: l10n.miningModeLabel, - ), - items: [ - DropdownMenuItem( - value: MiningMode.solo, - child: Text(l10n.miningModeSolo), - ), - DropdownMenuItem( - value: MiningMode.pool, - child: Text(l10n.miningModePool), - ), - ], - onChanged: (value) { - if (value == null) { - return; - } - setState(() { - _mode = value; - _dirty = true; - }); - }, - ), - const SizedBox(height: 16), - if (_mode == MiningMode.solo) ...[ - _InfoTile( - label: l10n.miningTargetLabel, - value: l10n.miningSoloTargetValue, - icon: Symbols.lan, - ), - const SizedBox(height: 12), - Text( - config.nodeConfig.mode == NodeMode.local - ? l10n.miningSoloHint - : l10n.miningSoloNeedsLocalNode, - style: theme.textTheme.bodyMedium?.copyWith( - color: config.nodeConfig.mode == NodeMode.local - ? theme.colorScheme.onSurfaceVariant - : theme.colorScheme.error, - ), - ), - ] else ...[ - TextFormField( - controller: _poolHostController, - decoration: InputDecoration( - labelText: l10n.hostLabel, - ), - onChanged: (_) => setState(() => _dirty = true), - ), - const SizedBox(height: 16), - TextFormField( - controller: _poolPortController, - keyboardType: TextInputType.number, - decoration: InputDecoration( - labelText: l10n.portLabel, - ), - onChanged: (_) => setState(() => _dirty = true), - ), - const SizedBox(height: 12), - Text( - l10n.miningPoolHint, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - const SizedBox(height: 20), - DropdownButtonFormField( - value: _cpuThreads.clamp(1, maxThreads), - decoration: InputDecoration( - labelText: l10n.miningThreadsLabel, - ), - items: [ - for (var value = 1; value <= maxThreads; value++) - DropdownMenuItem( - value: value, - child: Text(value.toString()), - ), - ], - onChanged: (value) { - if (value == null) { - return; - } - setState(() { - _cpuThreads = value; - _dirty = true; - }); - }, - ), - const SizedBox(height: 16), - TextFormField( - controller: _apiPortController, - keyboardType: TextInputType.number, - decoration: InputDecoration( - labelText: l10n.miningApiPortLabel, - ), - onChanged: (_) => setState(() => _dirty = true), - ), - const SizedBox(height: 16), - Row( - children: [ - FilledButton.icon( - onPressed: canSave ? _saveConfig : null, - icon: const Icon(Symbols.save), - label: Text(l10n.saveAction), - ), - const SizedBox(width: 12), - OutlinedButton.icon( - onPressed: _dirty ? _resetDraft : null, - icon: const Icon(Symbols.undo), - label: Text(l10n.miningResetDraftAction), - ), - ], - ), - ], - ), + FilledButton.icon( + onPressed: null, + icon: const Icon(Symbols.play_arrow), + label: Text(l10n.miningStartAction), ), - const SizedBox(height: 16), - _SectionCard( - title: l10n.miningControlsTitle, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Wrap( - spacing: 12, - runSpacing: 12, - children: [ - FilledButton.icon( - onPressed: null, - icon: const Icon(Symbols.play_arrow), - label: Text(l10n.miningStartAction), - ), - OutlinedButton.icon( - onPressed: null, - icon: const Icon(Symbols.stop), - label: Text(l10n.miningStopAction), - ), - OutlinedButton.icon( - onPressed: null, - icon: const Icon(Symbols.refresh), - label: Text(l10n.localNodeRestartAction), - ), - ], - ), - const SizedBox(height: 12), - Text( - l10n.miningBackendPending, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), + OutlinedButton.icon( + onPressed: null, + icon: const Icon(Symbols.stop), + label: Text(l10n.miningStopAction), + ), + OutlinedButton.icon( + onPressed: null, + icon: const Icon(Symbols.refresh), + label: Text(l10n.localNodeRestartAction), + ), + OutlinedButton.icon( + onPressed: () => _showSettingsDialog(maxThreads), + icon: const Icon(Symbols.settings), + label: Text(l10n.miningSettingsAction), ), ], ), - ), - SizedBox(width: wide ? 16 : 0, height: wide ? 0 : 16), - Expanded( - flex: wide ? 2 : 0, - child: Column( - children: [ - _SectionCard( - title: l10n.miningStatsTitle, - child: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - _StatTile( - label: l10n.transactionStatusLabel, - value: l10n.comingSoon, - ), - _StatTile( - label: l10n.miningHashrate10sLabel, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningHashrate1mLabel, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningLabelHashrate15m, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningJobsLabel, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningAcceptedSharesLabel, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningRejectedSharesLabel, - value: l10n.miningDataUnknownValue, - ), - _StatTile( - label: l10n.miningLabelUptime, - value: l10n.miningDataUnknownValue, - ), - ], - ), - ), - const SizedBox(height: 16), - _SectionCard( - title: l10n.miningCurrentConfigTitle, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _InfoTile( - label: l10n.miningWalletAddressLabel, - value: walletState.walletInfo?.address.isNotEmpty == true - ? walletState.walletInfo!.address - : l10n.miningNoWalletAddress, - icon: Symbols.account_balance_wallet, - ), - const SizedBox(height: 12), - _InfoTile( - label: l10n.miningModeLabel, - value: config.miningConfig.mode == MiningMode.solo - ? l10n.miningModeSolo - : '${config.miningConfig.poolHost}:${config.miningConfig.poolPort}', - icon: config.miningConfig.mode == MiningMode.solo - ? Symbols.lan - : Symbols.hub, - ), - const SizedBox(height: 12), - _InfoTile( - label: l10n.miningThreadsLabel, - value: config.miningConfig.cpuThreads.toString(), - icon: Symbols.memory, - ), - const SizedBox(height: 12), - _InfoTile( - label: l10n.miningApiPortLabel, - value: config.miningConfig.apiPort.toString(), - icon: Symbols.settings_ethernet, - ), - ], - ), - ), - const SizedBox(height: 16), - _SectionCard( - title: l10n.miningBundledTitle, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppFeatures.isMiningFlavor - ? l10n.miningBundledEnabled - : l10n.miningBundledDisabled, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 12), - Text( - l10n.miningBundledBody, - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], + const SizedBox(height: 12), + Text( + l10n.miningBackendPending, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), ), - ), - ], - ); - }, + ], + ), + ), + const SizedBox(height: 16), + _SectionCard( + title: l10n.miningCurrentConfigTitle, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _InfoTile( + label: l10n.miningWalletAddressLabel, + value: walletState.walletInfo?.address.isNotEmpty == true + ? walletState.walletInfo!.address + : l10n.miningNoWalletAddress, + icon: Symbols.account_balance_wallet, + ), + const SizedBox(height: 12), + _InfoTile( + label: l10n.miningModeLabel, + value: config.miningConfig.mode == MiningMode.solo + ? l10n.miningModeSolo + : '${config.miningConfig.poolHost}:${config.miningConfig.poolPort}', + icon: config.miningConfig.mode == MiningMode.solo + ? Symbols.lan + : Symbols.hub, + ), + const SizedBox(height: 12), + _InfoTile( + label: l10n.miningTargetLabel, + value: config.miningConfig.mode == MiningMode.solo + ? l10n.miningSoloTargetValue + : '${config.miningConfig.poolHost}:${config.miningConfig.poolPort}', + icon: Symbols.route, + ), + const SizedBox(height: 12), + _InfoTile( + label: l10n.miningThreadsLabel, + value: config.miningConfig.cpuThreads.toString(), + icon: Symbols.memory, + ), + const SizedBox(height: 12), + _InfoTile( + label: l10n.miningApiPortLabel, + value: config.miningConfig.apiPort.toString(), + icon: Symbols.settings_ethernet, + ), + ], + ), + ), + const SizedBox(height: 16), + _SectionCard( + title: l10n.miningStatsTitle, + child: Wrap( + spacing: 12, + runSpacing: 12, + children: [ + _StatTile( + label: l10n.transactionStatusLabel, + value: l10n.comingSoon, + ), + _StatTile( + label: l10n.miningHashrate10sLabel, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningHashrate1mLabel, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningLabelHashrate15m, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningJobsLabel, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningAcceptedSharesLabel, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningRejectedSharesLabel, + value: l10n.miningDataUnknownValue, + ), + _StatTile( + label: l10n.miningLabelUptime, + value: l10n.miningDataUnknownValue, + ), + ], + ), + ), + ], ), ], ), @@ -384,8 +212,6 @@ class _MiningScreenState extends ConsumerState { setState(() { _mode = config.mode; _cpuThreads = config.cpuThreads; - _poolHostController.text = config.poolHost; - _poolPortController.text = config.poolPort.toString(); _apiPortController.text = config.apiPort.toString(); _dirty = false; }); @@ -393,13 +219,8 @@ class _MiningScreenState extends ConsumerState { Future _saveConfig() async { final l10n = context.l10n; - final port = int.tryParse(_poolPortController.text.trim()); final apiPort = int.tryParse(_apiPortController.text.trim()); - if (_poolHostController.text.trim().isEmpty || - port == null || - port <= 0 || - apiPort == null || - apiPort <= 0) { + if (apiPort == null || apiPort <= 0) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.miningConfigInvalid)), ); @@ -409,8 +230,8 @@ class _MiningScreenState extends ConsumerState { final next = MiningConfig( mode: _mode, cpuThreads: _cpuThreads, - poolHost: _poolHostController.text.trim(), - poolPort: port, + poolHost: 'peya.cryptohash.top', + poolPort: 3333, apiPort: apiPort, ); await ref.read(appConfigControllerProvider.notifier).setMiningConfig(next); @@ -422,6 +243,155 @@ class _MiningScreenState extends ConsumerState { SnackBar(content: Text(l10n.miningSettingsSaved)), ); } + + Future _showSettingsDialog(int maxThreads) async { + final l10n = context.l10n; + final theme = Theme.of(context); + await showDialog( + context: context, + builder: (dialogContext) { + return AlertDialog( + backgroundColor: theme.colorScheme.surface, + title: Text(l10n.miningSettingsAction), + content: SizedBox( + width: 520, + child: StatefulBuilder( + builder: (context, setDialogState) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DropdownButtonFormField( + value: _mode, + dropdownColor: theme.colorScheme.surface, + decoration: InputDecoration( + labelText: l10n.miningModeLabel, + ), + items: [ + DropdownMenuItem( + value: MiningMode.solo, + child: Text(l10n.miningModeSolo), + ), + DropdownMenuItem( + value: MiningMode.pool, + child: Text(l10n.miningModePool), + ), + ], + onChanged: (value) { + if (value == null) { + return; + } + setState(() { + _mode = value; + _dirty = true; + }); + setDialogState(() {}); + }, + ), + const SizedBox(height: 16), + if (_mode == MiningMode.solo) ...[ + _InfoTile( + label: l10n.miningTargetLabel, + value: l10n.miningSoloTargetValue, + icon: Symbols.lan, + ), + const SizedBox(height: 12), + Text( + ref.read(appConfigControllerProvider).nodeConfig.mode == + NodeMode.local + ? l10n.miningSoloHint + : l10n.miningSoloNeedsLocalNode, + style: theme.textTheme.bodyMedium?.copyWith( + color: ref.read(appConfigControllerProvider).nodeConfig.mode == + NodeMode.local + ? theme.colorScheme.onSurfaceVariant + : theme.colorScheme.error, + ), + ), + ] else ...[ + _InfoTile( + label: l10n.miningTargetLabel, + value: 'peya.cryptohash.top:3333', + icon: Symbols.hub, + ), + const SizedBox(height: 12), + Text( + l10n.miningPoolFixedHint, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + const SizedBox(height: 20), + Text( + '${l10n.miningThreadsLabel}: $_cpuThreads', + style: theme.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + Slider( + value: _cpuThreads.toDouble(), + min: 1, + max: maxThreads.toDouble(), + divisions: math.max(0, maxThreads - 1), + label: _cpuThreads.toString(), + onChanged: (value) { + final next = value.round(); + setState(() { + _cpuThreads = next; + _dirty = true; + }); + setDialogState(() {}); + }, + ), + const SizedBox(height: 8), + TextFormField( + controller: _apiPortController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + labelText: l10n.miningApiPortLabel, + ), + onChanged: (_) { + setState(() => _dirty = true); + setDialogState(() {}); + }, + ), + ], + ); + }, + ), + ), + actions: [ + TextButton( + onPressed: () { + _resetDraft(); + Navigator.of(dialogContext).pop(); + }, + child: Text(l10n.miningResetDraftAction), + ), + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(), + child: Text(l10n.cancelAction), + ), + FilledButton( + onPressed: () { + final apiPort = int.tryParse(_apiPortController.text.trim()); + return apiPort != null && apiPort > 0; + }() + ? () async { + await _saveConfig(); + if (mounted) { + Navigator.of(dialogContext).pop(); + } + } + : null, + child: Text(l10n.saveAction), + ), + ], + ); + }, + ); + } } class _SectionCard extends StatelessWidget {