diff --git a/angular.json b/angular.json index f62b5e9..d57534b 100644 --- a/angular.json +++ b/angular.json @@ -38,7 +38,8 @@ ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", - "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" + "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", + "node_modules/bootstrap-table/dist/bootstrap-table.min.js" ], "styles": [ "src/styles.scss", diff --git a/app/main.ts b/app/main.ts index f764c86..4eb2faa 100644 --- a/app/main.ts +++ b/app/main.ts @@ -31,9 +31,11 @@ function createWindow(): BrowserWindow { width: size.width, height: size.height, webPreferences: { - nodeIntegration: true, - allowRunningInsecureContent: (serve), - contextIsolation: false + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: false, + allowRunningInsecureContent: true, + contextIsolation: true, + devTools: true }, icon: path.join(__dirname, 'assets/icons/favicon.ico') }); @@ -348,7 +350,7 @@ try { } }); - ipcMain.on('start-monerod', (event, configFilePath: string[]) => { + ipcMain.handle('start-monerod', (event, configFilePath: string[]) => { startMoneroDaemon(configFilePath); }) diff --git a/app/preload.js b/app/preload.js new file mode 100644 index 0000000..1802e4c --- /dev/null +++ b/app/preload.js @@ -0,0 +1,14 @@ +// preload.js +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('electronAPI', { + startMonerod: (args) => { + ipcRenderer.invoke('start-monerod', args); + }, + onMoneroStdout: (callback) => { + ipcRenderer.on('monero-stdout', callback); + }, + onMoneroClose: (callback) => { + ipcRenderer.on('monero-close', callback); + } +}); diff --git a/src/app/core/services/daemon/daemon.service.ts b/src/app/core/services/daemon/daemon.service.ts index 9cee10e..6cce6b0 100644 --- a/src/app/core/services/daemon/daemon.service.ts +++ b/src/app/core/services/daemon/daemon.service.ts @@ -106,12 +106,18 @@ export class DaemonService { constructor(private httpClient: HttpClient, private electronService: ElectronService) { this.openDbPromise = this.openDatabase(); this.settings = this.loadSettings(); + const wdw = (window as any); if (this.electronService.isElectron) { this.electronService.ipcRenderer.on('monero-close', (event, code: number | null) => { this.onClose(); }); } + else if (wdw.electronAPI && wdw.electronAPI.onMoneroClose) { + wdw.electronAPI.onMoneroClose((event: any, code: number) => { + this.onClose(); + }); + } } private onClose(): void { @@ -250,16 +256,22 @@ export class DaemonService { return; } + /* if (!this.electronService.isElectron) { console.error("Could not start monero daemon: not electron app"); return; } + */ this.starting = true; console.log("Starting daemon"); const settings = await this.getSettings(); - this.electronService.ipcRenderer.send('start-monerod', settings.toCommandOptions()); + + if (this.electronService.ipcRenderer) this.electronService.ipcRenderer.send('start-monerod', settings.toCommandOptions()); + else { + const wdw = (window as any).electronAPI.startMonerod(settings.toCommandOptions()); + } await this.delay(3000); @@ -348,19 +360,23 @@ export class DaemonService { public async getLastBlockHeader(fillPowHash: boolean = false): Promise { const response = await this.callRpc(new GetLastBlockHeaderRequest(fillPowHash)); - return BlockHeader.parse(response.block_header); + if (response.result && response.result.status == 'BUSY') { + throw new CoreIsBusyError(); + } + + return BlockHeader.parse(response.result.block_header); } public async getBlockHeaderByHash(hash: string, fillPowHash: boolean = false): Promise { const response = await this.callRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash)); - return BlockHeader.parse(response.block_header); + return BlockHeader.parse(response.result.block_header); } public async getBlockHeaderByHeight(height: number, fillPowHash: boolean = false): Promise { const response = await this.callRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash)); - return BlockHeader.parse(response.block_header); + return BlockHeader.parse(response.result.block_header); } public async getBlockHeadersRange(startHeight: number, endHeight: number, fillPowHash: boolean = false): Promise { @@ -410,7 +426,11 @@ export class DaemonService { this.raiseRpcError(response.error); } - const bans: any[] = response.bans; + if (!response.result) { + return []; + } + + const bans: any[] = response.result.bans; const result: Ban[] = []; if (bans) bans.forEach((ban: any) => result.push(Ban.parse(ban))); @@ -691,6 +711,7 @@ export class DaemonService { throw new Error(`Could not stop daemon: ${response.status}`); } + /* if (this.electronService.isElectron) { return; } @@ -698,6 +719,7 @@ export class DaemonService { this.daemonRunning = false; this.onDaemonStatusChanged.emit(false); this.onDaemonStopEnd.emit(); + */ } public async setLimit(limitDown: number, limitUp: number): Promise<{ limitDown: number, limitUp: number }> { diff --git a/src/app/core/services/monero-installer/monero-installer.service.ts b/src/app/core/services/monero-installer/monero-installer.service.ts index 4cb62a5..c72e284 100644 --- a/src/app/core/services/monero-installer/monero-installer.service.ts +++ b/src/app/core/services/monero-installer/monero-installer.service.ts @@ -7,7 +7,7 @@ import { ElectronService } from '../electron/electron.service'; export class MoneroInstallerService { constructor(private electronService: ElectronService) {} - downloadMonero(downloadUrl: string, destination: string): Promise { + public downloadMonero(downloadUrl: string, destination: string): Promise { return new Promise((resolve, reject) => { this.electronService.ipcRenderer.invoke('download-monero', downloadUrl, destination) .then(() => resolve()) diff --git a/src/app/pages/bans/bans.component.html b/src/app/pages/bans/bans.component.html index 03cef98..7baea7e 100644 --- a/src/app/pages/bans/bans.component.html +++ b/src/app/pages/bans/bans.component.html @@ -1,4 +1,18 @@ - +

Bans

+
+ +
+ + +
+
Seconds -
\ No newline at end of file + + + + + + \ No newline at end of file diff --git a/src/app/pages/bans/bans.component.ts b/src/app/pages/bans/bans.component.ts index 0a57fcd..b5eca76 100644 --- a/src/app/pages/bans/bans.component.ts +++ b/src/app/pages/bans/bans.component.ts @@ -1,7 +1,9 @@ -import { AfterViewInit, Component } from '@angular/core'; +import { AfterViewInit, Component, NgZone } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { NavbarService } from '../../shared/components/navbar/navbar.service'; import { DaemonService } from '../../core/services/daemon/daemon.service'; +import { NavbarLink } from '../../shared/components/navbar/navbar.model'; + @Component({ selector: 'app-bans', @@ -9,14 +11,33 @@ import { DaemonService } from '../../core/services/daemon/daemon.service'; styleUrl: './bans.component.scss' }) export class BansComponent implements AfterViewInit { - - constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService) { + public readonly navbarLinks: NavbarLink[] = [ + new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview', true), + new NavbarLink('pills-set-bans-tab', '#pills-set-bans', 'pills-set-bans', false, 'Set Bans', true) + ]; + public daemonRunning: boolean = false; + public get daemonChangingStatus(): boolean { + return this.daemonService.stopping || this.daemonService.starting; + } + + constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) { if (event.url != '/bans') return; this.onNavigationEnd(); } }) + + this.daemonService.isRunning().then((running) => { + this.daemonRunning = running + }); + + this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => { + this.daemonRunning = running; + + if (running) this.load(); + }); + } ngAfterViewInit(): void { @@ -24,15 +45,21 @@ export class BansComponent implements AfterViewInit { console.log('BansComponent AFTER VIEW INIT'); - setTimeout(() => { + this.ngZone.run(() => { + //const $ = require('jquery'); + //const bootstrapTable = require('bootstrap-table'); + const $table = $('#bansTable'); - $table.bootstrapTable({}); + $table.bootstrapTable({ + + }); $table.bootstrapTable('refreshOptions', { classes: 'table table-bordered table-hover table-dark table-striped' - }); + }); + $table.bootstrapTable('showLoading'); this.load(); - }, 500); + }); } private onNavigationEnd(): void { @@ -50,9 +77,13 @@ export class BansComponent implements AfterViewInit { 'host': ban.host, 'seconds': ban.seconds })); - + $table.bootstrapTable('hideLoading'); $table.bootstrapTable('load', bans); } + public async setBans() { + + } + } diff --git a/src/app/pages/blockchain/blockchain.component.html b/src/app/pages/blockchain/blockchain.component.html index 093123e..ea2098d 100644 --- a/src/app/pages/blockchain/blockchain.component.html +++ b/src/app/pages/blockchain/blockchain.component.html @@ -11,6 +11,449 @@ +
+ +
+ +
+
+

Block Header Details

+
+
+ @if(lastBlockHeader) { +
+
+
Block Information
+
    +
  • Block Size: {{ lastBlockHeader.blockSize }} bytes
  • +
  • Block Weight: {{ lastBlockHeader.blockWeight }} units
  • +
  • Height: {{ lastBlockHeader.height }}
  • +
  • Major Version: {{ lastBlockHeader.majorVersion }}
  • +
  • Minor Version: {{ lastBlockHeader.minorVersion }}
  • +
  • Nonce: {{ lastBlockHeader.nonce }}
  • +
  • Number of Transactions: {{ lastBlockHeader.numTxes }}
  • +
  • Reward: {{ lastBlockHeader.reward }} XMR
  • +
  • Timestamp: {{ lastBlockHeader.timestamp | date:'medium' }}
  • +
+
+
+
Hashes & Difficulty
+
    +
  • Block Hash: {{ lastBlockHeader.hash }}
  • +
  • Previous Hash: {{ lastBlockHeader.prevHash }}
  • +
  • PoW Hash: {{ lastBlockHeader.powHash }}
  • +
  • Miner Transaction Hash: {{ lastBlockHeader.minerTxHash }}
  • +
  • Cumulative Difficulty: {{ lastBlockHeader.cumulativeDifficulty }}
  • +
  • Wide Cumulative Difficulty: {{ lastBlockHeader.wideCumulativeDifficulty }}
  • +
  • Difficulty: {{ lastBlockHeader.difficulty }}
  • +
  • Wide Difficulty: {{ lastBlockHeader.wideDifficulty }}
  • +
+
+
+
+
+
Miscellaneous
+
    +
  • Orphan Status: {{ lastBlockHeader.orphanStatus ? 'Yes' : 'No' }}
  • +
  • Depth: {{ lastBlockHeader.depth }}
  • +
  • Long Term Weight: {{ lastBlockHeader.longTermWeight }}
  • +
+
+
+ } + @else { +
+
+
Block Information
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+
Hashes & Difficulty
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+
+
+
Miscellaneous
+
    +
  • +
  • +
  • +
+
+
+ } +
+
+
+
+ + +
+
+

Block Details

+
+
+ + +
+
+
Block Blob
+

{{ block.blob }}

+
+
+ + +
+
+
Block Header
+
+
+
    +
  • Block Size: {{ block.blockHeader.blockSize }} bytes
  • +
  • Block Weight: {{ block.blockHeader.blockWeight }} units
  • +
  • Height: {{ block.blockHeader.height }}
  • +
  • Hash: {{ block.blockHeader.hash }}
  • +
  • Previous Hash: {{ block.blockHeader.prevHash }}
  • +
  • Miner Transaction Hash: {{ block.blockHeader.minerTxHash }}
  • +
  • Difficulty: {{ block.blockHeader.difficulty }}
  • +
  • Timestamp: {{ block.blockHeader.timestamp | date:'medium' }}
  • +
+
+
+
+
+ + +
+
+
Block Additional Details
+
+
+
    +
  • Major Version: {{ block.details.majorVersion }}
  • +
  • Minor Version: {{ block.details.minorVersion }}
  • +
  • Timestamp: {{ block.details.timestamp | date:'medium' }}
  • +
  • Previous ID: {{ block.details.prevId }}
  • +
  • Nonce: {{ block.details.nonce }}
  • +
  • Transaction Hashes: +
      +
    • {{ hash }}
    • +
    +
  • +
+
+
+
+
+
+ + + +
+
+
Block Blob
+

+
+
+ +
+
+
Block Header
+
+
+
    +
  • +
  • +
  • +
  • +
+
+
+
+
+ +
+
+
Block Additional Details
+
+
+
    +
  • +
  • +
  • +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ + +
+ Get block information by hash +
+ +
+ + +
+ Add PoW hash to block header response +
+ +
+ + + Block hash +
+ +
+ + + Block height +
+
+
+
+ +
+ + + + +
+ +
+
+
+

Block Header Details

+
+
+ @if(blockHeader) { +
+
+
Block Information
+
    +
  • Block Size: {{ blockHeader.blockSize }} bytes
  • +
  • Block Weight: {{ blockHeader.blockWeight }} units
  • +
  • Height: {{ blockHeader.height }}
  • +
  • Major Version: {{ blockHeader.majorVersion }}
  • +
  • Minor Version: {{ blockHeader.minorVersion }}
  • +
  • Nonce: {{ blockHeader.nonce }}
  • +
  • Number of Transactions: {{ blockHeader.numTxes }}
  • +
  • Reward: {{ blockHeader.reward }} XMR
  • +
  • Timestamp: {{ blockHeader.timestamp | date:'medium' }}
  • +
+
+
+
Hashes & Difficulty
+
    +
  • Block Hash: {{ blockHeader.hash }}
  • +
  • Previous Hash: {{ blockHeader.prevHash }}
  • +
  • PoW Hash: {{ blockHeader.powHash }}
  • +
  • Miner Transaction Hash: {{ blockHeader.minerTxHash }}
  • +
  • Cumulative Difficulty: {{ blockHeader.cumulativeDifficulty }}
  • +
  • Wide Cumulative Difficulty: {{ blockHeader.wideCumulativeDifficulty }}
  • +
  • Difficulty: {{ blockHeader.difficulty }}
  • +
  • Wide Difficulty: {{ blockHeader.wideDifficulty }}
  • +
+
+
+
+
+
Miscellaneous
+
    +
  • Orphan Status: {{ blockHeader.orphanStatus ? 'Yes' : 'No' }}
  • +
  • Depth: {{ blockHeader.depth }}
  • +
  • Long Term Weight: {{ blockHeader.longTermWeight }}
  • +
+
+
+ } + @else { +
+
+
Block Information
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+
Hashes & Difficulty
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+
+
+
Miscellaneous
+
    +
  • +
  • +
  • +
+
+
+ } +
+
+ +
+ +
+
+
+
+ + +
+ Get block header information by hash +
+ +
+ + +
+ Add PoW hash to block header response +
+ +
+ + + Block header hash +
+ +
+ + + Block header height +
+
+
+
+ +
+ + + + +
+ +
+ + + + +
+
+
+ +
+ + + Number of blocks top pop from blockchain +
+
+
+
+ +
+ + + + +
+ + +
+ + + + + + +
+ + + + +
+ + + +
\ No newline at end of file diff --git a/src/app/pages/blockchain/blockchain.component.scss b/src/app/pages/blockchain/blockchain.component.scss index e69de29..a78fcd9 100644 --- a/src/app/pages/blockchain/blockchain.component.scss +++ b/src/app/pages/blockchain/blockchain.component.scss @@ -0,0 +1,8 @@ +.card-header { + background-color: #007bff; + color: white; +} + +.placeholder-glow .placeholder { + background-color: rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/src/app/pages/blockchain/blockchain.component.ts b/src/app/pages/blockchain/blockchain.component.ts index 7b91d97..ed4e179 100644 --- a/src/app/pages/blockchain/blockchain.component.ts +++ b/src/app/pages/blockchain/blockchain.component.ts @@ -1,38 +1,151 @@ -import { Component, NgZone } from '@angular/core'; +import { AfterViewInit, Component, NgZone } from '@angular/core'; import { NavbarLink } from '../../shared/components/navbar/navbar.model'; import { DaemonService } from '../../core/services/daemon/daemon.service'; +import { Block, BlockHeader } from '../../../common'; @Component({ selector: 'app-blockchain', templateUrl: './blockchain.component.html', styleUrl: './blockchain.component.scss' }) -export class BlockchainComponent { +export class BlockchainComponent implements AfterViewInit { public readonly navbarLinks: NavbarLink[]; public daemonRunning: boolean = false; + public lastBlockHeader?: BlockHeader; + public getLastBlockError: string = ''; + public block?: Block; + public getBlockByHash: boolean = false; + public getBlockHash: string = ''; + public getBlockHeight: number = 0; + public fillPoWHash: boolean = false; + public gettingLastBlock: boolean = false; + + public gettingBlock: boolean = false; + public blockHeader?: BlockHeader; + public getBlockHeaderByHash: boolean = false; + public getBlockHeaderHash: string = ''; + public getBlockHeaderHeight: number = 0; + + public getBlockHeaderError: string = ''; + public getBlockError: string = ''; + public gettingBlockHeader: boolean = false; + + public popBlocksNBlocks: number = 0; + public poppingBlocks: boolean = false; + public popBlocksError: string = ''; + public popBlocksResult?: number; + + public savingBlockchain: boolean = false; + public saveBlockchainError: string = ''; + public blockchainSaved: boolean = false; constructor(private daemonService: DaemonService, private ngZone: NgZone) { this.navbarLinks = [ - new NavbarLink('pills-last-block-header-tab', '#pills-last-block-header', 'pills-last-block-header', false, 'Last Block Header', true), + new NavbarLink('pills-last-block-header-tab', '#pills-last-block-header', 'pills-last-block-header', true, 'Last Block Header', true), new NavbarLink('pills-get-block-tab', '#pills-get-block', 'pills-get-block', false, 'Get Block', true), new NavbarLink('pills-get-block-header-tab', '#pills-get-block-header', 'pills-get-block-header', false, 'Get Block Header', true), new NavbarLink('pills-pop-blocks-tab', '#pills-pop-blocks', 'pills-pop-blocks', false, 'Pop Blocks', true), new NavbarLink('pills-save-bc-tab', '#pills-save-bc', 'pills-save-bc', false, 'Save Blockchain', true) - ] - - this.daemonService.isRunning().then((result: boolean) => this.daemonRunning = result); + ]; this.daemonService.onDaemonStatusChanged.subscribe((running) => { this.ngZone.run(() => { this.daemonRunning = running; + this.navbarLinks.forEach((link) => link.disabled = !running); }); }); + this.daemonService.isRunning().then((value: boolean) => { this.ngZone.run(() => { this.daemonRunning = value; + this.navbarLinks.forEach((link) => link.disabled = !value); }); }); } - + ngAfterViewInit(): void { + this.load(); + } + + public async load(): Promise { + await this.getLastBlockHeader(); + } + + private async getLastBlockHeader(): Promise { + this.gettingLastBlock = true; + try { + this.lastBlockHeader = await this.daemonService.getLastBlockHeader(true); + this.getLastBlockError = ''; + } + catch(error) { + console.error(error); + this.getLastBlockError = `${error}`; + } + this.gettingLastBlock = false; + } + + public async getBlock(): Promise { + this.gettingLastBlock = true; + try { + this.block = await this.daemonService.getBlock(this.getBlockByHash ? this.getBlockHash : this.getBlockHeight, this.fillPoWHash); + this.getBlockError = ''; + } + catch(error) { + console.error(error); + this.getBlockError = `${error}`; + } + + this.gettingLastBlock = false; + + } + + public async getBlockHeader(): Promise { + this.gettingBlockHeader = true; + + try { + if (this.getBlockHeaderByHash) { + this.blockHeader = await this.daemonService.getBlockHeaderByHash(this.getBlockHeaderHash, this.fillPoWHash); + } + else { + this.blockHeader = await this.daemonService.getBlockHeaderByHeight(this.getBlockHeaderHeight, this.fillPoWHash); + } + + this.getBlockHeaderError = ''; + } catch (error) { + console.error(error); + this.getBlockHeaderError = `${error}`; + } + + this.gettingBlockHeader = false; + } + + public async popBlocks(): Promise { + this.poppingBlocks = true; + try { + this.popBlocksResult = await this.daemonService.popBlocks(this.popBlocksNBlocks); + this.popBlocksError = ''; + } + catch(error) { + console.error(error); + this.popBlocksResult = undefined; + this.popBlocksError = `${error}`; + } + this.poppingBlocks = false; + } + + public async saveBlockchain(): Promise { + this.savingBlockchain = true; + + try { + await this.daemonService.saveBc(); + this.blockchainSaved = true; + } + catch(error) { + console.error(error); + this.blockchainSaved = false; + this.saveBlockchainError = `${error}`; + } + + this.savingBlockchain = false; + } } diff --git a/src/app/pages/detail/detail.component.ts b/src/app/pages/detail/detail.component.ts index 17517fd..616776f 100644 --- a/src/app/pages/detail/detail.component.ts +++ b/src/app/pages/detail/detail.component.ts @@ -7,8 +7,7 @@ import { NavbarService } from '../../shared/components/navbar/navbar.service'; import { NavigationEnd, Router } from '@angular/router'; import { DaemonInfo } from '../../../common/DaemonInfo'; -import * as $ from 'jquery'; -import * as bootstrapTable from 'bootstrap-table'; + import { LogsService } from '../logs/logs.service'; import { ElectronService } from '../../core/services'; @@ -75,7 +74,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { this.nodeType = 'unknown'; this.blockchainSize = '0 GB'; this.syncProgress = '0 %'; - this.isLoading = true; + this.isLoading = false; this.navbarLinks = [ new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview', true), @@ -104,18 +103,38 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { ngOnInit(): void { console.log('DetailComponent INIT'); + } ngAfterViewInit(): void { console.log('DetailComponent AFTER VIEW INIT'); this.navbarService.setLinks(this.navbarLinks); + setTimeout(() => { + this.ngZone.run(() => { + + const $table = $('#table'); + $table.bootstrapTable({}); + $table.bootstrapTable('refreshOptions', { + classes: 'table table-bordered table-hover table-dark table-striped' + }); + $table.bootstrapTable('showLoading'); + /* + $table.bootstrapTable('refreshOptions', { + classes: 'table table-bordered table-hover table-dark table-striped' + }); + */ + }); + }, 1000); + if (this.loadInterval != null) return; - - this.load().then(() => { - this.cards = this.createCards(); + + this.ngZone.run(() => { + this.load().then(() => { + this.cards = this.createCards(); + }); }); - + this.loadInterval = setInterval(() => { /* const $table = $('#table'); @@ -124,13 +143,17 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { classes: 'table table-bordered table-hover table-dark table-striped' }); */ - if (this.stoppingDaemon) return; - this.load().then(() => { - this.cards = this.createCards(); - }); - }, 5000); + this.ngZone.run(() => { + + this.load().then(() => { + this.cards = this.createCards(); + }); + }, 5000); + }) + + } ngOnDestroy(): void { @@ -190,13 +213,6 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { if(!this.electronService.isElectron) this.stoppingDaemon = false; } - private onNavigationEnd(): void { - this.load().then(() => { - //this.cards = this.createCards(); - }); - - } - private createLoadingCards(): Card[] { return [ new Card('Connection Status', this.connectionStatus, true), @@ -238,6 +254,10 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { } private async load(): Promise { + if (this.isLoading) { + return; + } + try { this.isLoading = true; this.daemonRunning = await this.daemonService.isRunning(); @@ -251,6 +271,11 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { this.navbarService.enableLinks(); const $table = $('#table'); + $table.bootstrapTable({}); + $table.bootstrapTable('refreshOptions', { + classes: 'table table-bordered table-hover table-dark table-striped' + }); + if (this.getPeers().length == 0) $table.bootstrapTable('showLoading'); this.syncInfo = await this.daemonService.syncInfo(); this.height = this.syncInfo.height; @@ -293,6 +318,8 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { //const blockchainPruned = false; this.nodeType = blockchainPruned ? 'pruned' : 'full'; $table.bootstrapTable('load', this.getPeers()); + $table.bootstrapTable('hideLoading'); + } catch(error) { console.error(error); diff --git a/src/app/pages/logs/logs.component.ts b/src/app/pages/logs/logs.component.ts index 13d36d5..9871eba 100644 --- a/src/app/pages/logs/logs.component.ts +++ b/src/app/pages/logs/logs.component.ts @@ -19,23 +19,22 @@ export class LogsComponent implements AfterViewInit { } private scrollToBottom(): void { - this.lines; - const terminalOutput = document.getElementById('terminalOutput'); - if (terminalOutput) { - terminalOutput.style.width = `${window.innerWidth}`; - console.log(`scrolling from ${terminalOutput.offsetTop} to ${terminalOutput.scrollHeight}`) - terminalOutput.scrollBy(0, terminalOutput.scrollHeight) - } + this.ngZone.run(() => { + this.lines; + const terminalOutput = document.getElementById('terminalOutput'); + if (terminalOutput) { + terminalOutput.style.width = `${window.innerWidth}`; + console.log(`scrolling from ${terminalOutput.offsetTop} to ${terminalOutput.scrollHeight}`) + terminalOutput.scrollBy(0, terminalOutput.scrollHeight) + } + }); } private onLog(): void { if (this.logTerminal) this.logTerminal.nativeElement.scrollTop = this.logTerminal.nativeElement.scrollHeight; // Scorri automaticamente in basso setTimeout(() => { - this.ngZone.run(() => { - this.scrollToBottom(); - }) - + this.scrollToBottom(); }, 100); } @@ -45,6 +44,8 @@ export class LogsComponent implements AfterViewInit { ngAfterViewInit(): void { this.navbarService.removeLinks(); - this.scrollToBottom(); - } + + setTimeout(() => { + this.scrollToBottom(); + }, 500); } } diff --git a/src/app/pages/logs/logs.service.ts b/src/app/pages/logs/logs.service.ts index 87b3224..25f61b0 100644 --- a/src/app/pages/logs/logs.service.ts +++ b/src/app/pages/logs/logs.service.ts @@ -11,10 +11,16 @@ export class LogsService { private readonly ansiRegex: RegExp = /\u001b\[[0-9;]*m/g; constructor(private electronService: ElectronService, private ngZone: NgZone) { + const wdw = (window as any); if (this.electronService.isElectron) { this.electronService.ipcRenderer.on('monero-stdout', (event, message: string) => this.log(message)); this.electronService.ipcRenderer.on('monero-stderr', (event, message: string) => this.log(message)); } + else if (wdw.electronAPI && wdw.electronAPI.onMoneroStdout) { + wdw.electronAPI.onMoneroStdout((event: any, message: string) => { + this.log(message); + }); + } } diff --git a/src/common/BlockHeader.ts b/src/common/BlockHeader.ts index f94aab1..6691ff8 100644 --- a/src/common/BlockHeader.ts +++ b/src/common/BlockHeader.ts @@ -25,8 +25,6 @@ }, */ -import { ThisReceiver } from "@angular/compiler"; - export class BlockHeader { public readonly blockSize: number; public readonly blockWeight: number; diff --git a/src/common/MinerTx.ts b/src/common/MinerTx.ts index 8522b43..c2ddb2e 100644 --- a/src/common/MinerTx.ts +++ b/src/common/MinerTx.ts @@ -4,9 +4,9 @@ export class MinerTx { public readonly vin: TxInput[]; public readonly vout: TxOutput[]; public readonly extra: number[]; - public readonly rctSignatures: RctSignatures; + public readonly rctSignatures?: RctSignatures; - constructor(version: number, unlockTime: number, vin: TxInput[], vout: TxOutput[], extra: number[], rctSignatures: RctSignatures) { + constructor(version: number, unlockTime: number, vin: TxInput[], vout: TxOutput[], extra: number[], rctSignatures?: RctSignatures) { this.version = version; this.unlockTime = unlockTime; this.vin = vin; @@ -21,8 +21,12 @@ export class MinerTx { const _vin: any[] | undefined = minerTx.vin; const _vout: any[] | undefined = minerTx.vout; const extra = minerTx.extra; - const rctSignatures = RctSignatures.parse(minerTx.rct_signatures); + let rctSignatures; + if (minerTx.rct_signatures) { + rctSignatures = RctSignatures.parse(minerTx.rct_signatures); + } + const vin: TxInput[] = []; const vout: TxOutput[] = []; @@ -93,33 +97,20 @@ export class RctSignatures { } export class TxOutputTarget { - public readonly taggedKey: TaggedKey; - - constructor(taggedKey: TaggedKey) - { - this.taggedKey = taggedKey; - } - - public static parse(target: any): TxOutputTarget { - const taggedKey = TaggedKey.parse(target.tagged_key); - - return new TxOutputTarget(taggedKey); - } -} - -export class TaggedKey { - public readonly key: string; + public readonly viewKey: string; public readonly viewTag: string; - constructor(key: string, viewTag: string) { - this.key = key; + constructor(viewKey: string, viewTag: string) + { + this.viewKey = viewKey; this.viewTag = viewTag; } - public static parse(taggedKey: any): TaggedKey { - const key = taggedKey.key; - const viewTag = taggedKey.view_tag; + public static parse(target: any): TxOutputTarget { + const viewKey = target.view_key ? target.view_key : ''; + const viewTag = target.view_tag ? target.view_tag : ''; - return new TaggedKey(key, viewTag); + return new TxOutputTarget(viewKey, viewTag); } } + diff --git a/src/polyfills.ts b/src/polyfills.ts index be961ca..01f7832 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -53,5 +53,7 @@ import 'zone.js'; // Included with Angular CLI. */ import 'jquery'; +import * as $$ from 'jquery'; import * as $ from 'jquery'; -import 'bootstrap-table'; +import * as bootstrapTable from 'bootstrap-table'; +//import 'bootstrap-table';