From 80e8a283f411d87d7923e658faa930a38e3dfd18 Mon Sep 17 00:00:00 2001 From: everoddandeven Date: Sun, 22 Sep 2024 15:55:03 +0200 Subject: [PATCH] Settings implementation --- angular.json | 10 +- app/main.ts | 87 +++- src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 4 +- src/app/bans/bans.component.ts | 22 +- .../core/services/daemon/daemon.service.ts | 72 +++- src/app/detail/detail.component.html | 10 +- src/app/detail/detail.component.ts | 148 ++++--- src/app/detail/detail.module.ts | 3 +- .../hard-fork-info.component.ts | 9 +- src/app/load/load.component.html | 9 + src/app/load/load.component.scss | 0 src/app/load/load.component.spec.ts | 23 ++ src/app/load/load.component.ts | 15 + src/app/mining/mining.component.ts | 42 +- src/app/navbar/navbar.component.html | 2 +- src/app/settings/settings-routing.module.ts | 16 + src/app/settings/settings.component.html | 380 ++++++++++++++++++ src/app/settings/settings.component.scss | 0 src/app/settings/settings.component.spec.ts | 23 ++ src/app/settings/settings.component.ts | 257 ++++++++++++ src/app/settings/settings.module.ts | 14 + src/app/sidebar/sidebar.component.ts | 1 + src/common/DaemonSettings.ts | 136 +++++++ src/common/SyncInfo.ts | 2 +- src/common/error.ts | 2 + src/common/error/CoreIsBusyError.ts | 8 + src/common/error/RpcError.ts | 9 + src/common/request.ts | 34 +- src/common/request/BannedRequest.ts | 2 + src/common/request/CalculatePoWHashRequest.ts | 1 + src/common/request/EmptyRpcRequest.ts | 6 + src/common/request/FlushCacheRequest.ts | 1 + src/common/request/FlushTxPoolRequest.ts | 1 + src/common/request/GenerateBlocksRequest.ts | 1 + .../request/GetAlternateChainsRequest.ts | 2 +- src/common/request/GetBansRequest.ts | 1 + src/common/request/GetBlockCountRequest.ts | 1 + src/common/request/GetBlockHashRequest.ts | 1 + .../request/GetBlockHeaderByHashRequest.ts | 1 + .../request/GetBlockHeaderByHeightRequest.ts | 1 + .../request/GetBlockHeadersRangeRequest.ts | 1 + src/common/request/GetBlockTemplateRequest.ts | 1 + src/common/request/GetCoinbaseTxSumRequest.ts | 26 ++ src/common/request/GetConnectionsRequest.ts | 1 + src/common/request/GetFeeEstimateRequest.ts | 1 + src/common/request/GetInfoRequest.ts | 1 + .../request/GetLastBlockHeaderRequest.ts | 1 + src/common/request/GetMinerDataRequest.ts | 2 +- .../request/GetOutputHistogramRequest.ts | 1 + src/common/request/GetTxPoolBacklogRequest.ts | 1 + src/common/request/GetVersionRequest.ts | 1 + src/common/request/HardForkInfoRequest.ts | 1 + src/common/request/JsonRPCRequest.ts | 1 + src/common/request/PruneBlockchainRequest.ts | 1 + src/common/request/RelayTxRequest.ts | 1 + src/common/request/SetBansRequest.ts | 1 + src/common/request/SubmitBlockRequest.ts | 1 + src/common/request/SyncInfoRequest.ts | 1 + src/index.html | 2 +- src/polyfills.ts | 4 + 61 files changed, 1307 insertions(+), 104 deletions(-) create mode 100644 src/app/load/load.component.html create mode 100644 src/app/load/load.component.scss create mode 100644 src/app/load/load.component.spec.ts create mode 100644 src/app/load/load.component.ts create mode 100644 src/app/settings/settings-routing.module.ts create mode 100644 src/app/settings/settings.component.html create mode 100644 src/app/settings/settings.component.scss create mode 100644 src/app/settings/settings.component.spec.ts create mode 100644 src/app/settings/settings.component.ts create mode 100644 src/app/settings/settings.module.ts create mode 100644 src/common/DaemonSettings.ts create mode 100644 src/common/error.ts create mode 100644 src/common/error/CoreIsBusyError.ts create mode 100644 src/common/error/RpcError.ts create mode 100644 src/common/request/EmptyRpcRequest.ts create mode 100644 src/common/request/GetCoinbaseTxSumRequest.ts diff --git a/angular.json b/angular.json index c598fe9..cad8f2f 100644 --- a/angular.json +++ b/angular.json @@ -36,16 +36,16 @@ "src/favicon.ico", "src/assets" ], - "styles": [ - "src/styles.scss", - "node_modules/bootstrap-icons/font/bootstrap-icons.css", - "node_modules/bootstrap-table/dist/bootstrap-table.min.css" - ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", "node_modules/bootstrap-table/dist/bootstrap-table.js" ], + "styles": [ + "src/styles.scss", + "node_modules/bootstrap-icons/font/bootstrap-icons.css", + "node_modules/bootstrap-table/dist/bootstrap-table.min.css" + ], "customWebpackConfig": { "path": "./angular.webpack.js", "replaceDuplicatePlugins": true diff --git a/app/main.ts b/app/main.ts index 17c3f2a..85e8a31 100644 --- a/app/main.ts +++ b/app/main.ts @@ -1,7 +1,11 @@ -import {app, BrowserWindow, screen} from 'electron'; +import {app, BrowserWindow, ipcMain, screen} from 'electron'; +import { ChildProcess, ChildProcessWithoutNullStreams, exec, spawn } from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; + +const monerodFilePath: string = "/home/sidney/Documenti/monero-x86_64-linux-gnu-v0.18.3.4/monerod"; + let win: BrowserWindow | null = null; const args = process.argv.slice(1), serve = args.some(val => val === '--serve'); @@ -19,7 +23,7 @@ function createWindow(): BrowserWindow { webPreferences: { nodeIntegration: true, allowRunningInsecureContent: (serve), - contextIsolation: false, + contextIsolation: false }, }); @@ -53,6 +57,81 @@ function createWindow(): BrowserWindow { return win; } +function execMoneroDaemon(configFilePath: string): ChildProcess { + const monerodPath = path.resolve(__dirname, 'path/to/monerod'); // Percorso del binario di monerod + //const command = `"${monerodPath}" --config-file "${configFilePath}"`; + const command = `/home/sidney/Documenti/monero-x86_64-linux-gnu-v0.18.3.4/monerod --testnet --fast-block-sync 1 --prune-blockchain --sync-pruned-blocks --confirm-external-bind --max-concurrency 1 --log-level 1 --rpc-access-control-origins=*`; + + const monerodProcess = exec(command, (error, stdout, stderr) => { + if (error) { + console.error(`Errore durante l'avvio di monerod: ${error.message}`); + return; + } + + if (stderr) { + console.error(`stderr: ${stderr}`); + return; + } + + console.log(`stdout: ${stdout}`); + }); + + // Gestisci l'output in tempo reale + if (monerodProcess.stdout == null) { + throw new Error("No stdout for monero process") + } + + if (monerodProcess.stderr == null) { + throw new Error("No stderr for monero process"); + } + + monerodProcess.stdout.on('data', (data) => { + console.log(`monerod stdout: ${data}`); + }); + + monerodProcess.stderr.on('data', (data) => { + console.error(`monerod stderr: ${data}`); + }); + + return monerodProcess; +} + +function startMoneroDaemon(configFilePath: string): ChildProcessWithoutNullStreams { + const monerodPath = path.resolve(__dirname, monerodFilePath); + + const args = [ + '--testnet', + '--fast-block-sync', '1', + '--prune-blockchain', + '--sync-pruned-blocks', + '--confirm-external-bind', + '--max-concurrency', '1', + '--log-level', '1', + '--rpc-access-control-origins=*' + ]; + + // Avvia il processo usando spawn + const monerodProcess = spawn(monerodPath, args); + + // Gestisci l'output di stdout in streaming + monerodProcess.stdout.on('data', (data) => { + console.log(`monerod stdout: ${data}`); + // Puoi anche inviare i log all'interfaccia utente tramite IPC + }); + + // Gestisci gli errori in stderr + monerodProcess.stderr.on('data', (data) => { + console.error(`monerod stderr: ${data}`); + }); + + // Gestisci la chiusura del processo + monerodProcess.on('close', (code) => { + console.log(`monerod chiuso con codice: ${code}`); + }); + + return monerodProcess; +} + try { // This method will be called when Electron has finished // initialization and is ready to create browser windows. @@ -77,6 +156,10 @@ try { } }); + ipcMain.on('start-monerod', (event, configFilePath) => { + startMoneroDaemon(configFilePath); + }) + } catch (e) { // Catch Error // throw e; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index fae3cea..1f640a4 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -5,6 +5,7 @@ import { PageNotFoundComponent } from './shared/components'; import { HomeRoutingModule } from './home/home-routing.module'; import { DetailRoutingModule } from './detail/detail-routing.module'; import { HardForkInfoRoutingModule } from './hard-fork-info/hard-fork-info-routing.module'; +import { SettingsModule } from './settings/settings.module'; const routes: Routes = [ { @@ -23,7 +24,8 @@ const routes: Routes = [ RouterModule.forRoot(routes, {}), HomeRoutingModule, DetailRoutingModule, - HardForkInfoRoutingModule + HardForkInfoRoutingModule, + SettingsModule ], exports: [RouterModule] }) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6ef3515..015259d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,6 +16,7 @@ import { DetailModule } from './detail/detail.module'; import { AppComponent } from './app.component'; import { SidebarComponent } from "./sidebar/sidebar.component"; +import { LoadComponent } from "./load/load.component"; import { BansModule } from './bans/bans.module'; import { NavbarComponent } from "./navbar/navbar.component"; import { MiningModule } from './mining/mining.module'; @@ -44,7 +45,8 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl } }), SidebarComponent, - NavbarComponent + NavbarComponent, + LoadComponent ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/bans/bans.component.ts b/src/app/bans/bans.component.ts index 73199e0..f2b4a77 100644 --- a/src/app/bans/bans.component.ts +++ b/src/app/bans/bans.component.ts @@ -20,17 +20,19 @@ export class BansComponent implements AfterViewInit { } ngAfterViewInit(): void { - console.log('BansComponent AFTER VIEW INIT'); + this.navbarService.removeNavbarLinks(); - setTimeout(() => { - const $table = $('#bansTable'); - $table.bootstrapTable({}); - $table.bootstrapTable('refreshOptions', { - classes: 'table table-bordered table-hover table-dark table-striped' - }); - this.load(); - - }, 500); + console.log('BansComponent AFTER VIEW INIT'); + + setTimeout(() => { + const $table = $('#bansTable'); + $table.bootstrapTable({}); + $table.bootstrapTable('refreshOptions', { + classes: 'table table-bordered table-hover table-dark table-striped' + }); + this.load(); + + }, 500); } private onNavigationEnd(): void { diff --git a/src/app/core/services/daemon/daemon.service.ts b/src/app/core/services/daemon/daemon.service.ts index 49a73b4..90aab60 100644 --- a/src/app/core/services/daemon/daemon.service.ts +++ b/src/app/core/services/daemon/daemon.service.ts @@ -16,7 +16,8 @@ import { PruneBlockchainRequest, CalculatePoWHashRequest, FlushCacheRequest, - GetMinerDataRequest + GetMinerDataRequest, + EmptyRpcRequest } from '../../../../common/request'; import { BlockTemplate } from '../../../../common/BlockTemplate'; import { GeneratedBlocks } from '../../../../common/GeneratedBlocks'; @@ -34,23 +35,65 @@ import { RelayTxRequest } from '../../../../common/request/RelayTxRequest'; import { TxBacklogEntry } from '../../../../common/TxBacklogEntry'; import { BlockchainPruneInfo } from '../../../../common/BlockchainPruneInfo'; import { MinerData } from '../../../../common/MinerData'; +import { CoreIsBusyError } from '../../../../common/error'; +import { ElectronService } from '../electron/electron.service'; @Injectable({ providedIn: 'root' }) export class DaemonService { + private readonly configFilePath: string = './config'; private url: string = "http://127.0.0.1:28081"; + //private url: string = "http://node2.monerodevs.org:28089"; + //private url: string = "https://testnet.xmr.ditatompel.com"; + //private url: string = "https://xmr.yemekyedim.com:18081"; + //private url: string = "https://moneronode.org:18081"; private readonly headers: { [key: string]: string } = { - 'Content-Type': 'application/json' + "Access-Control-Allow-Headers": "*", // this will allow all CORS requests + "Access-Control-Allow-Methods": 'POST,GET' // this states the allowed methods }; - constructor(private httpClient: HttpClient) { } + constructor(private httpClient: HttpClient, private electronService: ElectronService) { } private async callJsonRpc(params: JsonRPCRequest): Promise<{ [key: string]: any }> { return await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/json_rpc`, params.toDictionary(), this.headers)); } + public async startDaemon(): Promise { + if (await this.isRunning()) { + console.warn("Daemon already running"); + return; + } + + if (!this.electronService.isElectron) { + console.error("Could not start monero daemon: not electron app"); + return; + } + + console.log("Starting daemon"); + + this.electronService.ipcRenderer.send('start-monerod', this.configFilePath); + + console.log("Daemon started"); + + setTimeout(() => { + }, 500) + } + + public async isRunning(): Promise { + try { + const response = await this.callJsonRpc(new EmptyRpcRequest()); + console.log(response); + return true; + } + catch(error) { + console.error(error); + return false; + } + + } + public async getBlockCount(): Promise { const response = await this.callJsonRpc(new GetBlockCountRequest()); @@ -147,10 +190,15 @@ export class DaemonService { public async getBans(): Promise { const response = await this.callJsonRpc(new GetBansRequest()); + + if (response.error) { + this.raiseRpcError(response.error); + } + const bans: any[] = response.bans; const result: Ban[] = []; - bans.forEach((ban: any) => result.push(Ban.parse(ban))); + if (bans) bans.forEach((ban: any) => result.push(Ban.parse(ban))); return result; } @@ -249,8 +297,24 @@ export class DaemonService { public async getMinerData(): Promise { const response = await this.callJsonRpc(new GetMinerDataRequest()); + if (response.error) { + this.raiseRpcError(response.error); + } + return MinerData.parse(response.result); } + private raiseRpcError(error: { code: number, message: string }): void { + + if (error.code == -9) { + throw new CoreIsBusyError(); + } + else + { + throw new Error(error.message); + } + + } + } diff --git a/src/app/detail/detail.component.html b/src/app/detail/detail.component.html index 4d9c1ce..e59a78b 100644 --- a/src/app/detail/detail.component.html +++ b/src/app/detail/detail.component.html @@ -1,7 +1,15 @@ -
+ + +
+
+

Daemon not running

+

Start monero daemon

+ +
+ @for(card of cards; track card.header) {
{{card.header}}
diff --git a/src/app/detail/detail.component.ts b/src/app/detail/detail.component.ts index 3e43222..66881ac 100644 --- a/src/app/detail/detail.component.ts +++ b/src/app/detail/detail.component.ts @@ -1,7 +1,5 @@ -import { Component, OnInit, AfterViewInit } from '@angular/core'; +import { Component, OnInit, AfterViewInit, NgZone } from '@angular/core'; import { DaemonService } from '../core/services/daemon/daemon.service'; -import * as jquery from 'jquery'; -import * as bootstrapTable from 'bootstrap-table' import { SyncInfo } from '../../common/SyncInfo'; import { Peer } from '../../common/Peer'; import { NavbarLink } from '../navbar/navbar.model'; @@ -16,6 +14,7 @@ import { DaemonInfo } from '../../common/DaemonInfo'; }) export class DetailComponent implements OnInit, AfterViewInit { + public daemonRunning: boolean; private syncInfo?: SyncInfo; private daemonInfo?: DaemonInfo; private readonly navbarLinks: NavbarLink[]; @@ -37,9 +36,16 @@ export class DetailComponent implements OnInit, AfterViewInit { private nodeType: string; private syncProgress: string; + private isLoading: boolean; + + public get loading(): boolean { + return this.isLoading; + } + public cards: Card[]; - constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService) { + constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) { + this.daemonRunning = false; this.syncStatus = 'Not synced'; this.height = 0; this.targetHeight = 0; @@ -55,6 +61,7 @@ export class DetailComponent implements OnInit, AfterViewInit { this.nodeType = 'unknown'; this.blockchainSize = '0 GB'; this.syncProgress = '0 %'; + this.isLoading = true; this.navbarLinks = [ new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview'), @@ -77,27 +84,42 @@ export class DetailComponent implements OnInit, AfterViewInit { ngAfterViewInit(): void { console.log('DetailComponent AFTER VIEW INIT'); + this.navbarService.setNavbarLinks(this.navbarLinks); setTimeout(() => { - const $table = $('#table'); - $table.bootstrapTable({}); - $table.bootstrapTable('refreshOptions', { - classes: 'table table-bordered table-hover table-dark table-striped' - }); - this.Load(); + this.ngZone.run(() => { + const $table = $('#table'); + $table.bootstrapTable({}); + $table.bootstrapTable('refreshOptions', { + classes: 'table table-bordered table-hover table-dark table-striped' + }); + this.load(); + + }, 500); + }); + } - }, 500); + public async startDaemon(): Promise { + if (this.daemonRunning) { + console.warn("Daemon already running"); + return; + } + + await this.daemonService.startDaemon(); + this.daemonRunning = await this.daemonService.isRunning(); } private onNavigationEnd(): void { - this.Load().then(() => { + this.load().then(() => { this.cards = this.createCards(); - this.navbarService.setNavbarLinks(this.navbarLinks); }); } private createCards(): Card[] { + if (!this.daemonRunning) { + return [] + } return [ new Card('Connection Status', this.connectionStatus), new Card('Network Type', this.networkType), @@ -114,50 +136,64 @@ export class DetailComponent implements OnInit, AfterViewInit { ]; } - public async Load(): Promise { - const $table = $('#table'); + private async load(): Promise { + try { + this.isLoading = true; + this.daemonRunning = await this.daemonService.isRunning(); + + if (!this.daemonRunning) { + return; + } + + const $table = $('#table'); + + this.syncInfo = await this.daemonService.syncInfo(); + this.height = this.syncInfo.height; + this.targetHeight = this.syncInfo.targetHeight; + this.nextNeededPruningSeed = this.syncInfo.nextNeededPruningSeed; + + if (this.height > 0 && this.targetHeight == 0) { + this.targetHeight = this.height; + this.syncStatus = 'Daemon synced'; + } + else if (this.height > 0 && this.targetHeight > 0 && this.height == this.targetHeight) { + this.syncStatus = 'Daemon synced'; + } + + this.overview = this.syncInfo.overview; + + const blockCount = await this.daemonService.getBlockCount(); + + this.blockCount = blockCount.count; + + const version = await this.daemonService.getVersion(); + + this.version = `${version.version}`; + + this.daemonInfo = await this.daemonService.getInfo(); + + const capacity: number = this.daemonInfo.freeSpace + this.daemonInfo.databaseSize; + const diskUsage = parseInt(`${this.daemonInfo.databaseSize * 100 / capacity}`); + const blockchainSize = (this.daemonInfo.databaseSize / 1000 / 1000 / 1000).toFixed(2); + this.blockchainSize = `${blockchainSize} GB`; + this.diskUsage = `${diskUsage} %`; + this.networkType = this.daemonInfo.nettype; + this.connectionStatus = this.daemonInfo.offline ? 'offline' : 'online'; + this.txCount = this.daemonInfo.txCount; + this.poolSize = this.daemonInfo.txPoolSize; + this.version = this.daemonInfo.version; + this.syncProgress = `${(this.height*100/this.targetHeight).toFixed(2)} %`; + + //const blockchainPruned = await this.isBlockchainPruned(); + const blockchainPruned = false; + this.nodeType = blockchainPruned ? 'pruned' : 'full'; + $table.bootstrapTable('load', this.getPeers()); + } + catch(error) { + console.error(error); + } - this.syncInfo = await this.daemonService.syncInfo(); - this.height = this.syncInfo.height; - this.targetHeight = this.syncInfo.targetHeight; - this.nextNeededPruningSeed = this.syncInfo.nextNeededPruningSeed; - - if (this.height > 0 && this.targetHeight == 0) { - this.targetHeight = this.height; - this.syncStatus = 'Daemon synced'; - } - else if (this.height > 0 && this.targetHeight > 0 && this.height == this.targetHeight) { - this.syncStatus = 'Daemon synced'; - } - - this.overview = this.syncInfo.overview; - - const blockCount = await this.daemonService.getBlockCount(); - - this.blockCount = blockCount.count; - - const version = await this.daemonService.getVersion(); - - this.version = `${version.version}`; - - this.daemonInfo = await this.daemonService.getInfo(); - - const capacity: number = this.daemonInfo.freeSpace + this.daemonInfo.databaseSize; - const diskUsage = parseInt(`${this.daemonInfo.databaseSize * 100 / capacity}`); - const blockchainSize = (this.daemonInfo.databaseSize / 1000 / 1000 / 1000).toFixed(2); - this.blockchainSize = `${blockchainSize} GB`; - this.diskUsage = `${diskUsage} %`; - this.networkType = this.daemonInfo.nettype; - this.connectionStatus = this.daemonInfo.offline ? 'offline' : 'online'; - this.txCount = this.daemonInfo.txCount; - this.poolSize = this.daemonInfo.txPoolSize; - this.version = this.daemonInfo.version; - this.syncProgress = `${(this.height*100/this.targetHeight).toFixed(2)} %`; - - //const blockchainPruned = await this.isBlockchainPruned(); - const blockchainPruned = false; - this.nodeType = blockchainPruned ? 'pruned' : 'full'; - $table.bootstrapTable('load', this.getPeers()); + this.isLoading = false; } public async isBlockchainPruned(): Promise { diff --git a/src/app/detail/detail.module.ts b/src/app/detail/detail.module.ts index 7123cca..88369dc 100644 --- a/src/app/detail/detail.module.ts +++ b/src/app/detail/detail.module.ts @@ -5,9 +5,10 @@ import { DetailRoutingModule } from './detail-routing.module'; import { DetailComponent } from './detail.component'; import { SharedModule } from '../shared/shared.module'; +import { LoadComponent } from "../load/load.component"; @NgModule({ declarations: [DetailComponent], - imports: [CommonModule, SharedModule, DetailRoutingModule] + imports: [CommonModule, SharedModule, DetailRoutingModule, LoadComponent] }) export class DetailModule {} diff --git a/src/app/hard-fork-info/hard-fork-info.component.ts b/src/app/hard-fork-info/hard-fork-info.component.ts index d065d32..e03980e 100644 --- a/src/app/hard-fork-info/hard-fork-info.component.ts +++ b/src/app/hard-fork-info/hard-fork-info.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { AfterViewInit, Component } from '@angular/core'; import { DaemonService } from '../core/services/daemon/daemon.service'; import { NavigationEnd, Router } from '@angular/router'; import { NavbarService } from '../navbar/navbar.service'; @@ -8,7 +8,7 @@ import { NavbarService } from '../navbar/navbar.service'; templateUrl: './hard-fork-info.component.html', styleUrl: './hard-fork-info.component.scss' }) -export class HardForkInfoComponent { +export class HardForkInfoComponent implements AfterViewInit { public cards: Card[]; private earliestHeight: number; private enabled: boolean; @@ -36,10 +36,13 @@ export class HardForkInfoComponent { }); } + ngAfterViewInit(): void { + this.navbarService.removeNavbarLinks(); + } + private onNavigationEnd(): void { this.load().then(() => { this.cards = this.createCards(); - this.navbarService.removeNavbarLinks(); }); } diff --git a/src/app/load/load.component.html b/src/app/load/load.component.html new file mode 100644 index 0000000..89693be --- /dev/null +++ b/src/app/load/load.component.html @@ -0,0 +1,9 @@ +
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/app/load/load.component.scss b/src/app/load/load.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/load/load.component.spec.ts b/src/app/load/load.component.spec.ts new file mode 100644 index 0000000..62fef8f --- /dev/null +++ b/src/app/load/load.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoadComponent } from './load.component'; + +describe('LoadComponent', () => { + let component: LoadComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoadComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/load/load.component.ts b/src/app/load/load.component.ts new file mode 100644 index 0000000..222964e --- /dev/null +++ b/src/app/load/load.component.ts @@ -0,0 +1,15 @@ +import { NgIf } from '@angular/common'; +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'app-load', + standalone: true, + imports: [NgIf], + templateUrl: './load.component.html', + styleUrl: './load.component.scss' +}) +export class LoadComponent { + + @Input() public show: boolean = false; + +} diff --git a/src/app/mining/mining.component.ts b/src/app/mining/mining.component.ts index eff6c44..6ebbc40 100644 --- a/src/app/mining/mining.component.ts +++ b/src/app/mining/mining.component.ts @@ -6,6 +6,7 @@ import { NavigationEnd, Router } from '@angular/router'; import { NavbarLink } from '../navbar/navbar.model'; import { MineableTxBacklog } from '../../common/MineableTxBacklog'; import { Chain } from '../../common/Chain'; +import { CoreIsBusyError } from '../../common/error'; @Component({ selector: 'app-mining', @@ -15,7 +16,7 @@ import { Chain } from '../../common/Chain'; export class MiningComponent implements AfterViewInit { private readonly navbarLinks: NavbarLink[]; - + private coreBusy: boolean; private minerData?: MinerData; private majorVersion: number; @@ -40,6 +41,7 @@ export class MiningComponent implements AfterViewInit { this.alreadyGeneratedCoins = 0; this.alternateChains = []; this.cards = []; + this.coreBusy = false; this.navbarLinks = [ new NavbarLink('pills-miner-data-tab', '#pills-miner-data', 'miner-data', true, 'Miner Data'), @@ -57,6 +59,7 @@ export class MiningComponent implements AfterViewInit { ngAfterViewInit(): void { console.log('DetailComponent AFTER VIEW INIT'); + this.navbarService.setNavbarLinks(this.navbarLinks); setTimeout(() => { const $table = $('#chainsTable'); @@ -72,28 +75,39 @@ export class MiningComponent implements AfterViewInit { private onNavigationEnd(): void { this.load().then(() => { this.cards = this.createCards(); - this.navbarService.setNavbarLinks(this.navbarLinks) }); } private async load(): Promise { - this.minerData = await this.daemonService.getMinerData(); + try { + this.minerData = await this.daemonService.getMinerData(); + this.majorVersion = this.minerData.majorVersion; + this.height = this.minerData.height; + this.prevId = this.minerData.prevId; + this.seedHash = this.minerData.seedHash; + this.difficulty = this.minerData.difficulty; + this.medianWeight = this.minerData.medianWeight; + this.alreadyGeneratedCoins = this.minerData.alreadyGeneratedCoins; - this.majorVersion = this.minerData.majorVersion; - this.height = this.minerData.height; - this.prevId = this.minerData.prevId; - this.seedHash = this.minerData.seedHash; - this.difficulty = this.minerData.difficulty; - this.medianWeight = this.minerData.medianWeight; - this.alreadyGeneratedCoins = this.minerData.alreadyGeneratedCoins; + this.alternateChains = await this.daemonService.getAlternateChains(); - this.alternateChains = await this.daemonService.getAlternateChains(); - - const $table = $('#chainsTable'); - $table.bootstrapTable('load', this.getChains()); + const $table = $('#chainsTable'); + $table.bootstrapTable('load', this.getChains()); + } + catch(error) { + if (error instanceof CoreIsBusyError) { + this.coreBusy = true; + } + } + } private createCards(): Card[] { + if (this.coreBusy) { + return [ + new Card('Error', 'Core is busy') + ] + } return [ new Card('Major Fork Version', `${this.majorVersion}`), new Card('Current block height', `${this.height}`), diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index e81f0fc..c3f3eb8 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -6,7 +6,7 @@