Split peyawallet into lite and mining variants
This commit is contained in:
+63
-15
@@ -1,5 +1,5 @@
|
||||
name: build
|
||||
run-name: build wallet (peya=${{ inputs.peya_release_tag || 'latest' }}, monero_c=${{ inputs.monero_c_release_tag || 'latest' }})
|
||||
run-name: build wallet variants (peya=${{ inputs.peya_release_tag || 'latest' }}, monero_c=${{ inputs.monero_c_release_tag || 'latest' }}, xmrig=${{ inputs.xmrig_release_tag || 'latest' }})
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -12,6 +12,10 @@ on:
|
||||
description: monero_c release tag to bundle runtime from
|
||||
required: false
|
||||
default: latest
|
||||
xmrig_release_tag:
|
||||
description: xmrig release tag to bundle mining runtime from
|
||||
required: false
|
||||
default: latest
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
@@ -29,11 +33,20 @@ on:
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: Build Linux wallet
|
||||
name: Build Linux (${{ matrix.flavor }})
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- flavor: lite
|
||||
enable_mining: "false"
|
||||
- flavor: mining
|
||||
enable_mining: "true"
|
||||
env:
|
||||
PEYA_RELEASE_TAG: ${{ inputs.peya_release_tag }}
|
||||
MONERO_C_RELEASE_TAG: ${{ inputs.monero_c_release_tag }}
|
||||
XMRIG_RELEASE_TAG: ${{ inputs.xmrig_release_tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
@@ -82,33 +95,56 @@ jobs:
|
||||
MONERO_C_GITEA_PAT: ${{ secrets.MONERO_C_GITEA_PAT }}
|
||||
GITEA_PAT: ${{ secrets.PEYA_GITEA_PAT }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXTRA_ARGS=()
|
||||
if [ "${{ matrix.flavor }}" = "mining" ]; then
|
||||
EXTRA_ARGS+=(--xmrig-tag "${XMRIG_RELEASE_TAG:-latest}")
|
||||
fi
|
||||
python3 scripts/ci/stage_release_runtime.py \
|
||||
--platform linux \
|
||||
--peya-tag "${PEYA_RELEASE_TAG:-latest}" \
|
||||
--monero-c-tag "${MONERO_C_RELEASE_TAG:-latest}" \
|
||||
--repo-root .
|
||||
--repo-root . \
|
||||
"${EXTRA_ARGS[@]}"
|
||||
|
||||
- name: Fetch Dart/Flutter deps
|
||||
run: |
|
||||
flutter pub get
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Linux release
|
||||
run: |
|
||||
flutter build linux --release
|
||||
flutter build linux --release \
|
||||
--dart-define=PEYA_ENABLE_MINING=${{ matrix.enable_mining }} \
|
||||
--dart-define=PEYA_RELEASE_FLAVOR=${{ matrix.flavor }}
|
||||
|
||||
- name: Bundle xmrig
|
||||
if: ${{ matrix.flavor == 'mining' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -Dm755 external/miner/xmrig \
|
||||
build/linux/x64/release/bundle/external/miner/xmrig
|
||||
|
||||
- name: Upload Linux bundle
|
||||
uses: https://github.com/actions/upload-artifact@v3
|
||||
with:
|
||||
name: peyawallet-linux-release
|
||||
name: peyawallet-${{ matrix.flavor }}-linux-release
|
||||
path: build/linux/x64/release/bundle/**
|
||||
if-no-files-found: error
|
||||
|
||||
build-windows:
|
||||
name: Build Windows wallet
|
||||
name: Build Windows (${{ matrix.flavor }})
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- flavor: lite
|
||||
enable_mining: "false"
|
||||
- flavor: mining
|
||||
enable_mining: "true"
|
||||
env:
|
||||
PEYA_RELEASE_TAG: ${{ inputs.peya_release_tag }}
|
||||
MONERO_C_RELEASE_TAG: ${{ inputs.monero_c_release_tag }}
|
||||
XMRIG_RELEASE_TAG: ${{ inputs.xmrig_release_tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
@@ -140,30 +176,42 @@ jobs:
|
||||
MONERO_C_GITEA_PAT: ${{ secrets.PEYA_GITEA_PAT }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXTRA_ARGS=()
|
||||
if [ "${{ matrix.flavor }}" = "mining" ]; then
|
||||
EXTRA_ARGS+=(--xmrig-tag "${XMRIG_RELEASE_TAG:-latest}")
|
||||
fi
|
||||
python -u scripts/ci/stage_release_runtime.py \
|
||||
--platform windows \
|
||||
--peya-tag "${PEYA_RELEASE_TAG:-latest}" \
|
||||
--monero-c-tag "${MONERO_C_RELEASE_TAG:-latest}" \
|
||||
--repo-root .
|
||||
--repo-root . \
|
||||
"${EXTRA_ARGS[@]}"
|
||||
|
||||
- name: Show toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
flutter doctor -v
|
||||
run: flutter doctor -v
|
||||
|
||||
- name: Fetch Dart/Flutter deps
|
||||
shell: bash
|
||||
run: |
|
||||
flutter pub get
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Windows release
|
||||
shell: bash
|
||||
run: |
|
||||
flutter build windows --release
|
||||
flutter build windows --release \
|
||||
--dart-define=PEYA_ENABLE_MINING=${{ matrix.enable_mining }} \
|
||||
--dart-define=PEYA_RELEASE_FLAVOR=${{ matrix.flavor }}
|
||||
|
||||
- name: Bundle xmrig
|
||||
if: ${{ matrix.flavor == 'mining' }}
|
||||
shell: cmd
|
||||
run: |
|
||||
if not exist build\windows\x64\runner\Release\external\miner mkdir build\windows\x64\runner\Release\external\miner
|
||||
copy external\miner\xmrig.exe build\windows\x64\runner\Release\external\miner\xmrig.exe
|
||||
|
||||
- name: Upload Windows bundle
|
||||
uses: https://github.com/actions/upload-artifact@v3
|
||||
with:
|
||||
name: peyawallet-windows-release
|
||||
name: peyawallet-${{ matrix.flavor }}-windows-release
|
||||
path: build/windows/x64/runner/Release/**
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: release
|
||||
run-name: release wallet ${{ inputs.tag_name }}
|
||||
run-name: release wallet variants ${{ inputs.tag_name }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -19,6 +19,10 @@ on:
|
||||
description: monero_c release tag to bundle runtime from
|
||||
required: false
|
||||
default: latest
|
||||
xmrig_release_tag:
|
||||
description: xmrig release tag to bundle mining runtime from
|
||||
required: false
|
||||
default: latest
|
||||
release_name:
|
||||
description: Optional release title
|
||||
required: false
|
||||
@@ -121,12 +125,21 @@ jobs:
|
||||
echo "release_id=$(jq -r '.id' /tmp/release.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-linux:
|
||||
name: Build Linux release
|
||||
name: Build Linux release (${{ matrix.flavor }})
|
||||
needs: create-release
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- flavor: lite
|
||||
enable_mining: "false"
|
||||
- flavor: mining
|
||||
enable_mining: "true"
|
||||
env:
|
||||
PEYA_RELEASE_TAG: ${{ inputs.peya_release_tag }}
|
||||
MONERO_C_RELEASE_TAG: ${{ inputs.monero_c_release_tag }}
|
||||
XMRIG_RELEASE_TAG: ${{ inputs.xmrig_release_tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
@@ -178,26 +191,40 @@ jobs:
|
||||
MONERO_C_GITEA_PAT: ${{ secrets.MONERO_C_GITEA_PAT }}
|
||||
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXTRA_ARGS=()
|
||||
if [ "${{ matrix.flavor }}" = "mining" ]; then
|
||||
EXTRA_ARGS+=(--xmrig-tag "${XMRIG_RELEASE_TAG:-latest}")
|
||||
fi
|
||||
python3 scripts/ci/stage_release_runtime.py \
|
||||
--platform linux \
|
||||
--peya-tag "${PEYA_RELEASE_TAG:-latest}" \
|
||||
--monero-c-tag "${MONERO_C_RELEASE_TAG:-latest}" \
|
||||
--repo-root .
|
||||
--repo-root . \
|
||||
"${EXTRA_ARGS[@]}"
|
||||
|
||||
- name: Fetch Dart/Flutter deps
|
||||
run: |
|
||||
flutter pub get
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Linux release
|
||||
run: |
|
||||
flutter build linux --release
|
||||
flutter build linux --release \
|
||||
--dart-define=PEYA_ENABLE_MINING=${{ matrix.enable_mining }} \
|
||||
--dart-define=PEYA_RELEASE_FLAVOR=${{ matrix.flavor }}
|
||||
|
||||
- name: Bundle xmrig
|
||||
if: ${{ matrix.flavor == 'mining' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -Dm755 external/miner/xmrig \
|
||||
build/linux/x64/release/bundle/external/miner/xmrig
|
||||
|
||||
- name: Package Linux release
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tag_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
archive="/tmp/peyawallet-${TAG_NAME}-linux-x86_64.tar.gz"
|
||||
archive="/tmp/peyawallet-${{ matrix.flavor }}-${TAG_NAME}-linux-x86_64.tar.gz"
|
||||
tar -C build/linux/x64/release/bundle -czf "${archive}" .
|
||||
echo "ARCHIVE_PATH=${archive}" >> "$GITHUB_ENV"
|
||||
echo "ARCHIVE_NAME=$(basename "${archive}")" >> "$GITHUB_ENV"
|
||||
@@ -244,12 +271,21 @@ jobs:
|
||||
>/dev/null
|
||||
|
||||
build-windows:
|
||||
name: Build Windows release
|
||||
name: Build Windows release (${{ matrix.flavor }})
|
||||
needs: create-release
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- flavor: lite
|
||||
enable_mining: "false"
|
||||
- flavor: mining
|
||||
enable_mining: "true"
|
||||
env:
|
||||
PEYA_RELEASE_TAG: ${{ inputs.peya_release_tag }}
|
||||
MONERO_C_RELEASE_TAG: ${{ inputs.monero_c_release_tag }}
|
||||
XMRIG_RELEASE_TAG: ${{ inputs.xmrig_release_tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
@@ -282,26 +318,38 @@ jobs:
|
||||
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
EXTRA_ARGS=()
|
||||
if [ "${{ matrix.flavor }}" = "mining" ]; then
|
||||
EXTRA_ARGS+=(--xmrig-tag "${XMRIG_RELEASE_TAG:-latest}")
|
||||
fi
|
||||
python -u scripts/ci/stage_release_runtime.py \
|
||||
--platform windows \
|
||||
--peya-tag "${PEYA_RELEASE_TAG:-latest}" \
|
||||
--monero-c-tag "${MONERO_C_RELEASE_TAG:-latest}" \
|
||||
--repo-root .
|
||||
--repo-root . \
|
||||
"${EXTRA_ARGS[@]}"
|
||||
|
||||
- name: Show toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
flutter doctor -v
|
||||
run: flutter doctor -v
|
||||
|
||||
- name: Fetch Dart/Flutter deps
|
||||
shell: bash
|
||||
run: |
|
||||
flutter pub get
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Windows release
|
||||
shell: bash
|
||||
run: |
|
||||
flutter build windows --release
|
||||
flutter build windows --release \
|
||||
--dart-define=PEYA_ENABLE_MINING=${{ matrix.enable_mining }} \
|
||||
--dart-define=PEYA_RELEASE_FLAVOR=${{ matrix.flavor }}
|
||||
|
||||
- name: Bundle xmrig
|
||||
if: ${{ matrix.flavor == 'mining' }}
|
||||
shell: cmd
|
||||
run: |
|
||||
if not exist build\windows\x64\runner\Release\external\miner mkdir build\windows\x64\runner\Release\external\miner
|
||||
copy external\miner\xmrig.exe build\windows\x64\runner\Release\external\miner\xmrig.exe
|
||||
|
||||
- name: Package Windows release
|
||||
shell: bash
|
||||
@@ -309,7 +357,7 @@ jobs:
|
||||
TAG_NAME: ${{ inputs.tag_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
archive="C:/Windows/Temp/peyawallet-${TAG_NAME}-windows-x86_64.zip"
|
||||
archive="C:/Windows/Temp/peyawallet-${{ matrix.flavor }}-${TAG_NAME}-windows-x86_64.zip"
|
||||
powershell.exe -NoProfile -Command "if (Test-Path '${archive}') { Remove-Item '${archive}' -Force }; Compress-Archive -Path 'build/windows/x64/runner/Release/*' -DestinationPath '${archive}'"
|
||||
echo "ARCHIVE_PATH=${archive}" >> "$GITHUB_ENV"
|
||||
echo "ARCHIVE_NAME=$(basename "${archive}")" >> "$GITHUB_ENV"
|
||||
|
||||
@@ -31,6 +31,8 @@ migrate_working_dir/
|
||||
.pub/
|
||||
/build/
|
||||
/external/
|
||||
/third_party/daemon/
|
||||
/third_party/monero_c/
|
||||
|
||||
# Local working files
|
||||
codex_resume.txt
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
abstract final class AppFeatures {
|
||||
static const bool enableMining = bool.fromEnvironment(
|
||||
'PEYA_ENABLE_MINING',
|
||||
defaultValue: true,
|
||||
);
|
||||
|
||||
static const String releaseFlavor = String.fromEnvironment(
|
||||
'PEYA_RELEASE_FLAVOR',
|
||||
defaultValue: 'mining',
|
||||
);
|
||||
|
||||
static bool get isMiningFlavor => enableMining;
|
||||
}
|
||||
@@ -6,6 +6,8 @@ enum NodeMode { local, remote }
|
||||
|
||||
enum LanguagePreference { system, english, polish }
|
||||
|
||||
enum MiningMode { solo, pool }
|
||||
|
||||
class RemoteNode {
|
||||
const RemoteNode({
|
||||
required this.host,
|
||||
@@ -223,6 +225,61 @@ class SyncStatus {
|
||||
}
|
||||
}
|
||||
|
||||
class MiningConfig {
|
||||
const MiningConfig({
|
||||
required this.mode,
|
||||
required this.cpuThreads,
|
||||
required this.poolHost,
|
||||
required this.poolPort,
|
||||
required this.apiPort,
|
||||
});
|
||||
|
||||
final MiningMode mode;
|
||||
final int cpuThreads;
|
||||
final String poolHost;
|
||||
final int poolPort;
|
||||
final int apiPort;
|
||||
|
||||
MiningConfig copyWith({
|
||||
MiningMode? mode,
|
||||
int? cpuThreads,
|
||||
String? poolHost,
|
||||
int? poolPort,
|
||||
int? apiPort,
|
||||
}) {
|
||||
return MiningConfig(
|
||||
mode: mode ?? this.mode,
|
||||
cpuThreads: cpuThreads ?? this.cpuThreads,
|
||||
poolHost: poolHost ?? this.poolHost,
|
||||
poolPort: poolPort ?? this.poolPort,
|
||||
apiPort: apiPort ?? this.apiPort,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'mode': mode.name,
|
||||
'cpuThreads': cpuThreads,
|
||||
'poolHost': poolHost,
|
||||
'poolPort': poolPort,
|
||||
'apiPort': apiPort,
|
||||
};
|
||||
}
|
||||
|
||||
factory MiningConfig.fromJson(Map<String, dynamic> json) {
|
||||
return MiningConfig(
|
||||
mode: MiningMode.values.firstWhere(
|
||||
(value) => value.name == (json['mode'] as String? ?? 'solo'),
|
||||
orElse: () => MiningMode.solo,
|
||||
),
|
||||
cpuThreads: (json['cpuThreads'] as num?)?.toInt() ?? 1,
|
||||
poolHost: json['poolHost'] as String? ?? 'peya.cryptohash.top',
|
||||
poolPort: (json['poolPort'] as num?)?.toInt() ?? 3333,
|
||||
apiPort: (json['apiPort'] as num?)?.toInt() ?? 18091,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppConfig {
|
||||
const AppConfig({
|
||||
required this.lastWallet,
|
||||
@@ -235,6 +292,7 @@ class AppConfig {
|
||||
required this.languagePreference,
|
||||
required this.hiddenSubaddresses,
|
||||
required this.localNodeArgs,
|
||||
required this.miningConfig,
|
||||
required this.p2poolStratum,
|
||||
required this.p2poolP2p,
|
||||
required this.p2poolStartMining,
|
||||
@@ -251,6 +309,7 @@ class AppConfig {
|
||||
final LanguagePreference languagePreference;
|
||||
final Map<String, List<String>> hiddenSubaddresses;
|
||||
final List<String> localNodeArgs;
|
||||
final MiningConfig miningConfig;
|
||||
final String p2poolStratum;
|
||||
final String p2poolP2p;
|
||||
final bool p2poolStartMining;
|
||||
@@ -267,6 +326,7 @@ class AppConfig {
|
||||
LanguagePreference? languagePreference,
|
||||
Map<String, List<String>>? hiddenSubaddresses,
|
||||
List<String>? localNodeArgs,
|
||||
MiningConfig? miningConfig,
|
||||
String? p2poolStratum,
|
||||
String? p2poolP2p,
|
||||
bool? p2poolStartMining,
|
||||
@@ -283,6 +343,7 @@ class AppConfig {
|
||||
languagePreference: languagePreference ?? this.languagePreference,
|
||||
hiddenSubaddresses: hiddenSubaddresses ?? this.hiddenSubaddresses,
|
||||
localNodeArgs: localNodeArgs ?? this.localNodeArgs,
|
||||
miningConfig: miningConfig ?? this.miningConfig,
|
||||
p2poolStratum: p2poolStratum ?? this.p2poolStratum,
|
||||
p2poolP2p: p2poolP2p ?? this.p2poolP2p,
|
||||
p2poolStartMining: p2poolStartMining ?? this.p2poolStartMining,
|
||||
@@ -306,6 +367,13 @@ class AppConfig {
|
||||
languagePreference: LanguagePreference.system,
|
||||
hiddenSubaddresses: const {},
|
||||
localNodeArgs: const [],
|
||||
miningConfig: const MiningConfig(
|
||||
mode: MiningMode.solo,
|
||||
cpuThreads: 1,
|
||||
poolHost: 'peya.cryptohash.top',
|
||||
poolPort: 3333,
|
||||
apiPort: 18091,
|
||||
),
|
||||
p2poolStratum: '0.0.0.0:3333',
|
||||
p2poolP2p: '0.0.0.0:38889',
|
||||
p2poolStartMining: false,
|
||||
@@ -325,6 +393,7 @@ class AppConfig {
|
||||
'languagePreference': languagePreference.name,
|
||||
'hiddenSubaddresses': hiddenSubaddresses,
|
||||
'localNodeArgs': localNodeArgs,
|
||||
'miningConfig': miningConfig.toJson(),
|
||||
'p2poolStratum': p2poolStratum,
|
||||
'p2poolP2p': p2poolP2p,
|
||||
'p2poolStartMining': p2poolStartMining,
|
||||
@@ -349,6 +418,8 @@ class AppConfig {
|
||||
final p2poolStartMining = json['p2poolStartMining'] as bool? ?? false;
|
||||
final p2poolMiningThreads =
|
||||
(json['p2poolMiningThreads'] as num?)?.toInt() ?? 1;
|
||||
final miningConfigJson =
|
||||
json['miningConfig'] as Map<String, dynamic>? ?? const {};
|
||||
return AppConfig(
|
||||
lastWallet: json['lastWallet'] == null
|
||||
? null
|
||||
@@ -374,6 +445,7 @@ class AppConfig {
|
||||
),
|
||||
hiddenSubaddresses: hidden,
|
||||
localNodeArgs: localNodeArgs,
|
||||
miningConfig: MiningConfig.fromJson(miningConfigJson),
|
||||
p2poolStratum: p2poolStratum,
|
||||
p2poolP2p: p2poolP2p,
|
||||
p2poolStartMining: p2poolStartMining,
|
||||
|
||||
@@ -345,6 +345,31 @@
|
||||
"miningThreadsLabel": "CPU threads",
|
||||
"miningSettingsSaved": "Mining settings saved.",
|
||||
"miningCurrentConfigTitle": "Current configuration",
|
||||
"miningScreenSubtitle": "Configure the CPU miner release now. Process control and live XMRig stats land in the next step.",
|
||||
"miningSourceTitle": "Source",
|
||||
"miningModeLabel": "Mining mode",
|
||||
"miningModeSolo": "Solo to local daemon",
|
||||
"miningModePool": "Pool",
|
||||
"miningTargetLabel": "Target",
|
||||
"miningSoloTargetValue": "127.0.0.1:17750 (local Peya daemon)",
|
||||
"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.",
|
||||
"miningApiPortLabel": "XMRig API port",
|
||||
"miningResetDraftAction": "Reset draft",
|
||||
"miningConfigInvalid": "Enter a valid pool host and ports.",
|
||||
"miningControlsTitle": "Controls",
|
||||
"miningBackendPending": "Miner process control will be enabled in the next step. This screen already stores the target config and release layout.",
|
||||
"miningStatsTitle": "Stats",
|
||||
"miningHashrate10sLabel": "Hashrate (10s)",
|
||||
"miningHashrate1mLabel": "Hashrate (1m)",
|
||||
"miningJobsLabel": "Jobs",
|
||||
"miningAcceptedSharesLabel": "Accepted shares",
|
||||
"miningRejectedSharesLabel": "Rejected shares",
|
||||
"miningBundledTitle": "Bundled miner",
|
||||
"miningBundledEnabled": "This build includes the bundled XMRig binary.",
|
||||
"miningBundledDisabled": "This release flavor does not include the bundled miner.",
|
||||
"miningBundledBody": "Mining releases bundle XMRig from the dedicated Peya xmrig repo. Lite releases hide the tab entirely.",
|
||||
"miningStartMiningEnabled": "Enabled ({threads} threads)",
|
||||
"@miningStartMiningEnabled": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -1694,6 +1694,156 @@ abstract class AppLocalizations {
|
||||
/// **'Current configuration'**
|
||||
String get miningCurrentConfigTitle;
|
||||
|
||||
/// No description provided for @miningScreenSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Configure the CPU miner release now. Process control and live XMRig stats land in the next step.'**
|
||||
String get miningScreenSubtitle;
|
||||
|
||||
/// No description provided for @miningSourceTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Source'**
|
||||
String get miningSourceTitle;
|
||||
|
||||
/// No description provided for @miningModeLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mining mode'**
|
||||
String get miningModeLabel;
|
||||
|
||||
/// No description provided for @miningModeSolo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Solo to local daemon'**
|
||||
String get miningModeSolo;
|
||||
|
||||
/// No description provided for @miningModePool.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pool'**
|
||||
String get miningModePool;
|
||||
|
||||
/// No description provided for @miningTargetLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Target'**
|
||||
String get miningTargetLabel;
|
||||
|
||||
/// No description provided for @miningSoloTargetValue.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'127.0.0.1:17750 (local Peya daemon)'**
|
||||
String get miningSoloTargetValue;
|
||||
|
||||
/// No description provided for @miningSoloHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Solo mode will target the local daemon bundled with the wallet.'**
|
||||
String get miningSoloHint;
|
||||
|
||||
/// No description provided for @miningSoloNeedsLocalNode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Solo mode expects the wallet to use the local node.'**
|
||||
String get miningSoloNeedsLocalNode;
|
||||
|
||||
/// No description provided for @miningPoolHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Default pool is preconfigured for Peya and can be adjusted here.'**
|
||||
String get miningPoolHint;
|
||||
|
||||
/// No description provided for @miningApiPortLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'XMRig API port'**
|
||||
String get miningApiPortLabel;
|
||||
|
||||
/// No description provided for @miningResetDraftAction.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reset draft'**
|
||||
String get miningResetDraftAction;
|
||||
|
||||
/// No description provided for @miningConfigInvalid.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter a valid pool host and ports.'**
|
||||
String get miningConfigInvalid;
|
||||
|
||||
/// No description provided for @miningControlsTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Controls'**
|
||||
String get miningControlsTitle;
|
||||
|
||||
/// No description provided for @miningBackendPending.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Miner process control will be enabled in the next step. This screen already stores the target config and release layout.'**
|
||||
String get miningBackendPending;
|
||||
|
||||
/// No description provided for @miningStatsTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stats'**
|
||||
String get miningStatsTitle;
|
||||
|
||||
/// No description provided for @miningHashrate10sLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hashrate (10s)'**
|
||||
String get miningHashrate10sLabel;
|
||||
|
||||
/// No description provided for @miningHashrate1mLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hashrate (1m)'**
|
||||
String get miningHashrate1mLabel;
|
||||
|
||||
/// No description provided for @miningJobsLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Jobs'**
|
||||
String get miningJobsLabel;
|
||||
|
||||
/// No description provided for @miningAcceptedSharesLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Accepted shares'**
|
||||
String get miningAcceptedSharesLabel;
|
||||
|
||||
/// No description provided for @miningRejectedSharesLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Rejected shares'**
|
||||
String get miningRejectedSharesLabel;
|
||||
|
||||
/// No description provided for @miningBundledTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Bundled miner'**
|
||||
String get miningBundledTitle;
|
||||
|
||||
/// No description provided for @miningBundledEnabled.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This build includes the bundled XMRig binary.'**
|
||||
String get miningBundledEnabled;
|
||||
|
||||
/// No description provided for @miningBundledDisabled.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This release flavor does not include the bundled miner.'**
|
||||
String get miningBundledDisabled;
|
||||
|
||||
/// No description provided for @miningBundledBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mining releases bundle XMRig from the dedicated Peya xmrig repo. Lite releases hide the tab entirely.'**
|
||||
String get miningBundledBody;
|
||||
|
||||
/// No description provided for @miningStartMiningEnabled.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -850,6 +850,89 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get miningCurrentConfigTitle => 'Current configuration';
|
||||
|
||||
@override
|
||||
String get miningScreenSubtitle =>
|
||||
'Configure the CPU miner release now. Process control and live XMRig stats land in the next step.';
|
||||
|
||||
@override
|
||||
String get miningSourceTitle => 'Source';
|
||||
|
||||
@override
|
||||
String get miningModeLabel => 'Mining mode';
|
||||
|
||||
@override
|
||||
String get miningModeSolo => 'Solo to local daemon';
|
||||
|
||||
@override
|
||||
String get miningModePool => 'Pool';
|
||||
|
||||
@override
|
||||
String get miningTargetLabel => 'Target';
|
||||
|
||||
@override
|
||||
String get miningSoloTargetValue => '127.0.0.1:17750 (local Peya daemon)';
|
||||
|
||||
@override
|
||||
String get miningSoloHint =>
|
||||
'Solo mode will target the local daemon bundled with the wallet.';
|
||||
|
||||
@override
|
||||
String get miningSoloNeedsLocalNode =>
|
||||
'Solo mode expects the wallet to use the local node.';
|
||||
|
||||
@override
|
||||
String get miningPoolHint =>
|
||||
'Default pool is preconfigured for Peya and can be adjusted here.';
|
||||
|
||||
@override
|
||||
String get miningApiPortLabel => 'XMRig API port';
|
||||
|
||||
@override
|
||||
String get miningResetDraftAction => 'Reset draft';
|
||||
|
||||
@override
|
||||
String get miningConfigInvalid => 'Enter a valid pool host and ports.';
|
||||
|
||||
@override
|
||||
String get miningControlsTitle => 'Controls';
|
||||
|
||||
@override
|
||||
String get miningBackendPending =>
|
||||
'Miner process control will be enabled in the next step. This screen already stores the target config and release layout.';
|
||||
|
||||
@override
|
||||
String get miningStatsTitle => 'Stats';
|
||||
|
||||
@override
|
||||
String get miningHashrate10sLabel => 'Hashrate (10s)';
|
||||
|
||||
@override
|
||||
String get miningHashrate1mLabel => 'Hashrate (1m)';
|
||||
|
||||
@override
|
||||
String get miningJobsLabel => 'Jobs';
|
||||
|
||||
@override
|
||||
String get miningAcceptedSharesLabel => 'Accepted shares';
|
||||
|
||||
@override
|
||||
String get miningRejectedSharesLabel => 'Rejected shares';
|
||||
|
||||
@override
|
||||
String get miningBundledTitle => 'Bundled miner';
|
||||
|
||||
@override
|
||||
String get miningBundledEnabled =>
|
||||
'This build includes the bundled XMRig binary.';
|
||||
|
||||
@override
|
||||
String get miningBundledDisabled =>
|
||||
'This release flavor does not include the bundled miner.';
|
||||
|
||||
@override
|
||||
String get miningBundledBody =>
|
||||
'Mining releases bundle XMRig from the dedicated Peya xmrig repo. Lite releases hide the tab entirely.';
|
||||
|
||||
@override
|
||||
String miningStartMiningEnabled(Object threads) {
|
||||
return 'Enabled ($threads threads)';
|
||||
|
||||
@@ -854,6 +854,89 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get miningCurrentConfigTitle => 'Aktualna konfiguracja';
|
||||
|
||||
@override
|
||||
String get miningScreenSubtitle =>
|
||||
'Tu konfigurujesz wariant CPU miner. Sterowanie procesem i żywe statystyki XMRig dojdą w następnym kroku.';
|
||||
|
||||
@override
|
||||
String get miningSourceTitle => 'Źródło';
|
||||
|
||||
@override
|
||||
String get miningModeLabel => 'Tryb kopania';
|
||||
|
||||
@override
|
||||
String get miningModeSolo => 'Solo do lokalnego daemona';
|
||||
|
||||
@override
|
||||
String get miningModePool => 'Pool';
|
||||
|
||||
@override
|
||||
String get miningTargetLabel => 'Cel';
|
||||
|
||||
@override
|
||||
String get miningSoloTargetValue => '127.0.0.1:17750 (lokalny daemon Peya)';
|
||||
|
||||
@override
|
||||
String get miningSoloHint =>
|
||||
'Tryb solo będzie kierował kopanie do lokalnego daemona bundlowanego z walletem.';
|
||||
|
||||
@override
|
||||
String get miningSoloNeedsLocalNode =>
|
||||
'Tryb solo zakłada, że portfel korzysta z lokalnego noda.';
|
||||
|
||||
@override
|
||||
String get miningPoolHint =>
|
||||
'Domyślny pool dla Peya jest już wpisany i można go tu zmienić.';
|
||||
|
||||
@override
|
||||
String get miningApiPortLabel => 'Port API XMRig';
|
||||
|
||||
@override
|
||||
String get miningResetDraftAction => 'Przywróć szkic';
|
||||
|
||||
@override
|
||||
String get miningConfigInvalid => 'Podaj poprawny host poola i porty.';
|
||||
|
||||
@override
|
||||
String get miningControlsTitle => 'Sterowanie';
|
||||
|
||||
@override
|
||||
String get miningBackendPending =>
|
||||
'Sterowanie procesem minera zostanie dopięte w następnym kroku. Ten ekran zapisuje już docelową konfigurację i układ releasu.';
|
||||
|
||||
@override
|
||||
String get miningStatsTitle => 'Statystyki';
|
||||
|
||||
@override
|
||||
String get miningHashrate10sLabel => 'Hashrate (10s)';
|
||||
|
||||
@override
|
||||
String get miningHashrate1mLabel => 'Hashrate (1m)';
|
||||
|
||||
@override
|
||||
String get miningJobsLabel => 'Joby';
|
||||
|
||||
@override
|
||||
String get miningAcceptedSharesLabel => 'Zaakceptowane udziały';
|
||||
|
||||
@override
|
||||
String get miningRejectedSharesLabel => 'Odrzucone udziały';
|
||||
|
||||
@override
|
||||
String get miningBundledTitle => 'Bundlowany miner';
|
||||
|
||||
@override
|
||||
String get miningBundledEnabled =>
|
||||
'Ten build zawiera bundlowaną binarkę XMRig.';
|
||||
|
||||
@override
|
||||
String get miningBundledDisabled =>
|
||||
'Ten wariant releasu nie zawiera bundlowanego minera.';
|
||||
|
||||
@override
|
||||
String get miningBundledBody =>
|
||||
'Warianty mining bundle\'ują XMRig z dedykowanego repo Peya xmrig. Wariant lite całkowicie ukrywa ten tab.';
|
||||
|
||||
@override
|
||||
String miningStartMiningEnabled(Object threads) {
|
||||
return 'Włączony ($threads wątków)';
|
||||
|
||||
@@ -345,6 +345,31 @@
|
||||
"miningThreadsLabel": "Wątki CPU",
|
||||
"miningSettingsSaved": "Ustawienia mining zapisane.",
|
||||
"miningCurrentConfigTitle": "Aktualna konfiguracja",
|
||||
"miningScreenSubtitle": "Tu konfigurujesz wariant CPU miner. Sterowanie procesem i żywe statystyki XMRig dojdą w następnym kroku.",
|
||||
"miningSourceTitle": "Źródło",
|
||||
"miningModeLabel": "Tryb kopania",
|
||||
"miningModeSolo": "Solo do lokalnego daemona",
|
||||
"miningModePool": "Pool",
|
||||
"miningTargetLabel": "Cel",
|
||||
"miningSoloTargetValue": "127.0.0.1:17750 (lokalny daemon Peya)",
|
||||
"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ć.",
|
||||
"miningApiPortLabel": "Port API XMRig",
|
||||
"miningResetDraftAction": "Przywróć szkic",
|
||||
"miningConfigInvalid": "Podaj poprawny host poola i porty.",
|
||||
"miningControlsTitle": "Sterowanie",
|
||||
"miningBackendPending": "Sterowanie procesem minera zostanie dopięte w następnym kroku. Ten ekran zapisuje już docelową konfigurację i układ releasu.",
|
||||
"miningStatsTitle": "Statystyki",
|
||||
"miningHashrate10sLabel": "Hashrate (10s)",
|
||||
"miningHashrate1mLabel": "Hashrate (1m)",
|
||||
"miningJobsLabel": "Joby",
|
||||
"miningAcceptedSharesLabel": "Zaakceptowane udziały",
|
||||
"miningRejectedSharesLabel": "Odrzucone udziały",
|
||||
"miningBundledTitle": "Bundlowany miner",
|
||||
"miningBundledEnabled": "Ten build zawiera bundlowaną binarkę XMRig.",
|
||||
"miningBundledDisabled": "Ten wariant releasu nie zawiera bundlowanego minera.",
|
||||
"miningBundledBody": "Warianty mining bundle'ują XMRig z dedykowanego repo Peya xmrig. Wariant lite całkowicie ukrywa ten tab.",
|
||||
"miningStartMiningEnabled": "Włączony ({threads} wątków)",
|
||||
"@miningStartMiningEnabled": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -46,6 +46,48 @@ class AppConfigController extends StateNotifier<AppConfig> {
|
||||
await updateConfig(state.copyWith(localNodeArgs: args));
|
||||
}
|
||||
|
||||
Future<void> setMiningConfig(MiningConfig config) async {
|
||||
await updateConfig(state.copyWith(miningConfig: config));
|
||||
}
|
||||
|
||||
Future<void> setMiningMode(MiningMode mode) async {
|
||||
await updateConfig(
|
||||
state.copyWith(miningConfig: state.miningConfig.copyWith(mode: mode)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMiningCpuThreads(int value) async {
|
||||
await updateConfig(
|
||||
state.copyWith(
|
||||
miningConfig: state.miningConfig.copyWith(cpuThreads: value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMiningPoolHost(String value) async {
|
||||
await updateConfig(
|
||||
state.copyWith(
|
||||
miningConfig: state.miningConfig.copyWith(poolHost: value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMiningPoolPort(int value) async {
|
||||
await updateConfig(
|
||||
state.copyWith(
|
||||
miningConfig: state.miningConfig.copyWith(poolPort: value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMiningApiPort(int value) async {
|
||||
await updateConfig(
|
||||
state.copyWith(
|
||||
miningConfig: state.miningConfig.copyWith(apiPort: value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setP2poolStratum(String value) async {
|
||||
await updateConfig(state.copyWith(p2poolStratum: value));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app_features.dart';
|
||||
import '../l10n/app_localizations_ext.dart';
|
||||
import '../widgets/status_bar.dart';
|
||||
import 'account_screen.dart';
|
||||
@@ -25,16 +26,54 @@ class _HomeShellState extends ConsumerState<HomeShell> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final screens = <Widget>[
|
||||
const AccountScreen(),
|
||||
const SendScreen(),
|
||||
const ReceiveScreen(),
|
||||
const StakingScreen(),
|
||||
const MiningScreen(),
|
||||
const TransactionsScreen(),
|
||||
const SettingsScreen(),
|
||||
final items = <_NavItem>[
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.account_box),
|
||||
label: l10n.accountTitle,
|
||||
screen: const AccountScreen(),
|
||||
),
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.call_made),
|
||||
label: l10n.sendTitle,
|
||||
screen: const SendScreen(),
|
||||
),
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.call_received),
|
||||
label: l10n.receiveTitle,
|
||||
screen: const ReceiveScreen(),
|
||||
),
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.chart_data),
|
||||
label: l10n.stakingTitle,
|
||||
screen: const StakingScreen(),
|
||||
),
|
||||
if (AppFeatures.enableMining)
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.memory),
|
||||
label: l10n.miningTitle,
|
||||
screen: const MiningScreen(),
|
||||
),
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.list_alt),
|
||||
label: l10n.transactionsTitle,
|
||||
screen: const TransactionsScreen(),
|
||||
),
|
||||
_NavItem(
|
||||
icon: const Icon(Symbols.settings),
|
||||
label: l10n.settingsTitle,
|
||||
screen: const SettingsScreen(),
|
||||
),
|
||||
];
|
||||
|
||||
final selectedIndex = _selectedIndex.clamp(0, items.length - 1);
|
||||
if (selectedIndex != _selectedIndex) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() => _selectedIndex = selectedIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
@@ -42,7 +81,7 @@ class _HomeShellState extends ConsumerState<HomeShell> {
|
||||
minWidth: 88,
|
||||
minExtendedWidth: 232,
|
||||
extended: true,
|
||||
selectedIndex: _selectedIndex,
|
||||
selectedIndex: selectedIndex,
|
||||
onDestinationSelected: (index) =>
|
||||
setState(() => _selectedIndex = index),
|
||||
labelType: NavigationRailLabelType.none,
|
||||
@@ -50,42 +89,39 @@ class _HomeShellState extends ConsumerState<HomeShell> {
|
||||
padding: EdgeInsets.fromLTRB(18, 18, 18, 10),
|
||||
child: _BrandHeader(),
|
||||
),
|
||||
destinations: [
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.account_box),
|
||||
label: Text(l10n.accountTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.call_made),
|
||||
label: Text(l10n.sendTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.call_received),
|
||||
label: Text(l10n.receiveTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.chart_data),
|
||||
label: Text(l10n.stakingTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.memory),
|
||||
label: Text(l10n.miningTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.list_alt),
|
||||
label: Text(l10n.transactionsTitle)),
|
||||
NavigationRailDestination(
|
||||
icon: const Icon(Symbols.settings),
|
||||
label: Text(l10n.settingsTitle)),
|
||||
],
|
||||
destinations: items
|
||||
.map(
|
||||
(item) => NavigationRailDestination(
|
||||
icon: item.icon,
|
||||
label: Text(item.label),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
trailing: const Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: StatusBar(),
|
||||
),
|
||||
),
|
||||
const VerticalDivider(width: 1),
|
||||
Expanded(child: screens[_selectedIndex]),
|
||||
Expanded(child: items[selectedIndex].screen),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NavItem {
|
||||
const _NavItem({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.screen,
|
||||
});
|
||||
|
||||
final Widget icon;
|
||||
final String label;
|
||||
final Widget screen;
|
||||
}
|
||||
|
||||
class _BrandHeader extends StatelessWidget {
|
||||
const _BrandHeader();
|
||||
|
||||
|
||||
@@ -1,14 +1,535 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
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';
|
||||
import 'placeholder_screen.dart';
|
||||
|
||||
class MiningScreen extends StatelessWidget {
|
||||
class MiningScreen extends ConsumerStatefulWidget {
|
||||
const MiningScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<MiningScreen> createState() => _MiningScreenState();
|
||||
}
|
||||
|
||||
class _MiningScreenState extends ConsumerState<MiningScreen> {
|
||||
late MiningMode _mode;
|
||||
late int _cpuThreads;
|
||||
late final TextEditingController _poolHostController;
|
||||
late final TextEditingController _poolPortController;
|
||||
late final TextEditingController _apiPortController;
|
||||
bool _dirty = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
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();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return PlaceholderScreen(title: l10n.miningTitle);
|
||||
final theme = Theme.of(context);
|
||||
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),
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 1120),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.miningTitle,
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
l10n.miningScreenSubtitle,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
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(
|
||||
children: [
|
||||
_SectionCard(
|
||||
title: l10n.miningSourceTitle,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
DropdownButtonFormField<MiningMode>(
|
||||
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<int>(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _resetDraft() {
|
||||
final config = ref.read(appConfigControllerProvider).miningConfig;
|
||||
setState(() {
|
||||
_mode = config.mode;
|
||||
_cpuThreads = config.cpuThreads;
|
||||
_poolHostController.text = config.poolHost;
|
||||
_poolPortController.text = config.poolPort.toString();
|
||||
_apiPortController.text = config.apiPort.toString();
|
||||
_dirty = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _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) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.miningConfigInvalid)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final next = MiningConfig(
|
||||
mode: _mode,
|
||||
cpuThreads: _cpuThreads,
|
||||
poolHost: _poolHostController.text.trim(),
|
||||
poolPort: port,
|
||||
apiPort: apiPort,
|
||||
);
|
||||
await ref.read(appConfigControllerProvider.notifier).setMiningConfig(next);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() => _dirty = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.miningSettingsSaved)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionCard extends StatelessWidget {
|
||||
const _SectionCard({required this.title, required this.child});
|
||||
|
||||
final String title;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
child,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StatTile extends StatelessWidget {
|
||||
const _StatTile({required this.label, required this.value});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Container(
|
||||
width: 180,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.65),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
value,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InfoTile extends StatelessWidget {
|
||||
const _InfoTile({
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, size: 20, color: theme.colorScheme.primary),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
SelectableText(
|
||||
value,
|
||||
style: theme.textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,14 @@ def extract_member_from_zip(url: str, member_name: str, destination: Path, token
|
||||
write_bytes(destination, archive.read(member), executable=True)
|
||||
|
||||
|
||||
def stage_linux(base_url: str, monero_c_tag: str, peya_tag: str, token: str | None, root: Path) -> None:
|
||||
def stage_linux(
|
||||
base_url: str,
|
||||
monero_c_tag: str,
|
||||
peya_tag: str,
|
||||
xmrig_tag: str | None,
|
||||
token: str | None,
|
||||
root: Path,
|
||||
) -> None:
|
||||
log("Staging Linux runtime")
|
||||
monero_release = release_metadata(base_url, "tiamak/monero_c", monero_c_tag, token)
|
||||
peya_release = release_metadata(base_url, "tiamak/Peya", peya_tag, token)
|
||||
@@ -135,10 +142,25 @@ def stage_linux(base_url: str, monero_c_tag: str, peya_tag: str, token: str | No
|
||||
peya_url = asset_download_url(base_url, peya_release, peya_asset, token)
|
||||
extract_member_from_tar(peya_url, "peyad", daemon_dir / "peyad", token)
|
||||
|
||||
if xmrig_tag:
|
||||
miner_dir = root / "external" / "miner"
|
||||
ensure_clean_dir(miner_dir)
|
||||
xmrig_release = release_metadata(base_url, "tiamak/xmrig", xmrig_tag, token)
|
||||
xmrig_asset = f"xmrig-peya-{xmrig_release['tag_name']}-linux-x86_64.tar.gz"
|
||||
xmrig_url = asset_download_url(base_url, xmrig_release, xmrig_asset, token)
|
||||
extract_member_from_tar(xmrig_url, "xmrig", miner_dir / "xmrig", token)
|
||||
|
||||
print(f"Staged Linux runtime from monero_c {monero_release['tag_name']} and Peya {peya_release['tag_name']}")
|
||||
|
||||
|
||||
def stage_windows(base_url: str, monero_c_tag: str, peya_tag: str, token: str | None, root: Path) -> None:
|
||||
def stage_windows(
|
||||
base_url: str,
|
||||
monero_c_tag: str,
|
||||
peya_tag: str,
|
||||
xmrig_tag: str | None,
|
||||
token: str | None,
|
||||
root: Path,
|
||||
) -> None:
|
||||
log("Staging Windows runtime")
|
||||
monero_release = release_metadata(base_url, "tiamak/monero_c", monero_c_tag, token)
|
||||
peya_release = release_metadata(base_url, "tiamak/Peya", peya_tag, token)
|
||||
@@ -161,6 +183,14 @@ def stage_windows(base_url: str, monero_c_tag: str, peya_tag: str, token: str |
|
||||
peya_url = asset_download_url(base_url, peya_release, peya_asset, token)
|
||||
extract_member_from_zip(peya_url, "peyad.exe", daemon_dir / "peyad.exe", token)
|
||||
|
||||
if xmrig_tag:
|
||||
miner_dir = root / "external" / "miner"
|
||||
ensure_clean_dir(miner_dir)
|
||||
xmrig_release = release_metadata(base_url, "tiamak/xmrig", xmrig_tag, token)
|
||||
xmrig_asset = f"xmrig-peya-{xmrig_release['tag_name']}-windows-x86_64.zip"
|
||||
xmrig_url = asset_download_url(base_url, xmrig_release, xmrig_asset, token)
|
||||
extract_member_from_zip(xmrig_url, "xmrig.exe", miner_dir / "xmrig.exe", token)
|
||||
|
||||
print(f"Staged Windows runtime from monero_c {monero_release['tag_name']} and Peya {peya_release['tag_name']}")
|
||||
|
||||
|
||||
@@ -169,6 +199,7 @@ def main() -> int:
|
||||
parser.add_argument("--platform", choices=("linux", "windows"), required=True)
|
||||
parser.add_argument("--monero-c-tag", default="latest")
|
||||
parser.add_argument("--peya-tag", default="latest")
|
||||
parser.add_argument("--xmrig-tag")
|
||||
parser.add_argument("--gitea-base-url", default=os.environ.get("GITEA_BASE_URL", DEFAULT_GITEA_BASE))
|
||||
parser.add_argument("--repo-root", default=".")
|
||||
args = parser.parse_args()
|
||||
@@ -178,9 +209,23 @@ def main() -> int:
|
||||
|
||||
try:
|
||||
if args.platform == "linux":
|
||||
stage_linux(args.gitea_base_url, args.monero_c_tag, args.peya_tag, token, root)
|
||||
stage_linux(
|
||||
args.gitea_base_url,
|
||||
args.monero_c_tag,
|
||||
args.peya_tag,
|
||||
args.xmrig_tag,
|
||||
token,
|
||||
root,
|
||||
)
|
||||
else:
|
||||
stage_windows(args.gitea_base_url, args.monero_c_tag, args.peya_tag, token, root)
|
||||
stage_windows(
|
||||
args.gitea_base_url,
|
||||
args.monero_c_tag,
|
||||
args.peya_tag,
|
||||
args.xmrig_tag,
|
||||
token,
|
||||
root,
|
||||
)
|
||||
except Exception as exc:
|
||||
log(f"stage_release_runtime.py failed: {exc}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user