Transactions and outputs pages

This commit is contained in:
everoddandeven 2024-09-24 00:28:24 +02:00
parent cd600e06cd
commit ef3d5b616e
24 changed files with 259 additions and 16 deletions

View file

@ -6,6 +6,7 @@ import { HomeRoutingModule } from './home/home-routing.module';
import { DetailRoutingModule } from './detail/detail-routing.module'; import { DetailRoutingModule } from './detail/detail-routing.module';
import { HardForkInfoRoutingModule } from './hard-fork-info/hard-fork-info-routing.module'; import { HardForkInfoRoutingModule } from './hard-fork-info/hard-fork-info-routing.module';
import { SettingsModule } from './settings/settings.module'; import { SettingsModule } from './settings/settings.module';
import { TransactionsModule } from './transactions/transactions.module';
const routes: Routes = [ const routes: Routes = [
{ {
@ -24,6 +25,7 @@ const routes: Routes = [
RouterModule.forRoot(routes, {}), RouterModule.forRoot(routes, {}),
HomeRoutingModule, HomeRoutingModule,
DetailRoutingModule, DetailRoutingModule,
TransactionsModule,
HardForkInfoRoutingModule, HardForkInfoRoutingModule,
SettingsModule SettingsModule
], ],

View file

@ -1,7 +1,7 @@
<app-navbar></app-navbar> <app-navbar></app-navbar>
<div class="d-flex"> <div class="d-flex">
<app-sidebar></app-sidebar> <app-sidebar></app-sidebar>
<div class="d-flex"> <div class="col-md-10">
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>
</div> </div>

View file

@ -20,6 +20,8 @@ import { LoadComponent } from "./load/load.component";
import { BansModule } from './bans/bans.module'; import { BansModule } from './bans/bans.module';
import { NavbarComponent } from "./navbar/navbar.component"; import { NavbarComponent } from "./navbar/navbar.component";
import { MiningModule } from './mining/mining.module'; import { MiningModule } from './mining/mining.module';
import { TransactionsModule } from './transactions/transactions.module';
import { OutputsModule } from './outputs/outputs.module';
// AoT requires an exported function for factories // AoT requires an exported function for factories
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json'); const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
@ -36,6 +38,9 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
DetailModule, DetailModule,
BansModule, BansModule,
MiningModule, MiningModule,
TransactionsModule,
OutputsModule,
TranslateModule,
AppRoutingModule, AppRoutingModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {

View file

@ -5,9 +5,13 @@
<div class="row d-flex"> <div class="row d-flex">
<div *ngIf="!daemonRunning" class="h-100 p-5 text-bg-dark rounded-3 m-4"> <div *ngIf="!daemonRunning" class="h-100 p-5 text-bg-dark rounded-3 m-4">
<h2>Daemon not running</h2> <h2><i class="bi bi-exclamation-diamond"></i> Daemon not running</h2>
<p>Start monero daemon</p> <p>Start monero daemon</p>
<button class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button> <button *ngIf="!startingDaemon" class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button>
<button *ngIf="startingDaemon" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Starting daemon
</button>
</div> </div>
@for(card of cards; track card.header) { @for(card of cards; track card.header) {

View file

@ -1,4 +1,4 @@
import { Component, OnInit, AfterViewInit, NgZone } from '@angular/core'; import { Component, OnInit, AfterViewInit, NgZone, OnDestroy } from '@angular/core';
import { DaemonService } from '../core/services/daemon/daemon.service'; import { DaemonService } from '../core/services/daemon/daemon.service';
import { SyncInfo } from '../../common/SyncInfo'; import { SyncInfo } from '../../common/SyncInfo';
import { Peer } from '../../common/Peer'; import { Peer } from '../../common/Peer';
@ -12,9 +12,10 @@ import { DaemonInfo } from '../../common/DaemonInfo';
templateUrl: './detail.component.html', templateUrl: './detail.component.html',
styleUrls: ['./detail.component.scss'] styleUrls: ['./detail.component.scss']
}) })
export class DetailComponent implements OnInit, AfterViewInit { export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
public daemonRunning: boolean; public daemonRunning: boolean;
public startingDaemon: boolean;
private syncInfo?: SyncInfo; private syncInfo?: SyncInfo;
private daemonInfo?: DaemonInfo; private daemonInfo?: DaemonInfo;
private readonly navbarLinks: NavbarLink[]; private readonly navbarLinks: NavbarLink[];
@ -38,6 +39,8 @@ export class DetailComponent implements OnInit, AfterViewInit {
private isLoading: boolean; private isLoading: boolean;
private loadInterval?: any;
public get loading(): boolean { public get loading(): boolean {
return this.isLoading; return this.isLoading;
} }
@ -46,6 +49,7 @@ export class DetailComponent implements OnInit, AfterViewInit {
constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) { constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
this.daemonRunning = false; this.daemonRunning = false;
this.startingDaemon = false;
this.syncStatus = 'Not synced'; this.syncStatus = 'Not synced';
this.height = 0; this.height = 0;
this.targetHeight = 0; this.targetHeight = 0;
@ -86,8 +90,11 @@ export class DetailComponent implements OnInit, AfterViewInit {
console.log('DetailComponent AFTER VIEW INIT'); console.log('DetailComponent AFTER VIEW INIT');
this.navbarService.setNavbarLinks(this.navbarLinks); this.navbarService.setNavbarLinks(this.navbarLinks);
setTimeout(() => { this.loadInterval = setInterval(() => {
this.ngZone.run(() => { this.ngZone.run(() => {
if (this.isLoading) {
return;
}
const $table = $('#table'); const $table = $('#table');
$table.bootstrapTable({}); $table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', { $table.bootstrapTable('refreshOptions', {
@ -95,8 +102,16 @@ export class DetailComponent implements OnInit, AfterViewInit {
}); });
this.load(); this.load();
}, 500);
}); });
}, 5000);
}
ngOnDestroy(): void {
console.log("DetailComponent ON DESTROY");
if(this.loadInterval != null) {
clearInterval(this.loadInterval);
}
} }
public async startDaemon(): Promise<void> { public async startDaemon(): Promise<void> {
@ -104,10 +119,18 @@ export class DetailComponent implements OnInit, AfterViewInit {
console.warn("Daemon already running"); console.warn("Daemon already running");
return; return;
} }
this.startingDaemon = true;
try {
await this.daemonService.startDaemon(); await this.daemonService.startDaemon();
this.daemonRunning = await this.daemonService.isRunning(); this.daemonRunning = await this.daemonService.isRunning();
} }
catch(error) {
console.error(error);
}
this.startingDaemon = false;
}
private onNavigationEnd(): void { private onNavigationEnd(): void {
this.load().then(() => { this.load().then(() => {

View file

@ -55,11 +55,13 @@
<div *ngIf="coreBusy" class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column text-center"> <div *ngIf="coreBusy" class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column text-center">
<main class="px-3"> <main class="px-3">
<h1>Core is busy.</h1> <h1><i class="bi bi-exclamation-diamond"></i> Core is busy.</h1>
<p class="lead">Mining capabilities are not available during daemon sync.</p> <p class="lead">Mining capabilities are not available during daemon sync.</p>
<!--
<p class="lead"> <p class="lead">
<a href="#" class="btn btn-lg btn-light fw-bold border-white bg-white">Learn more</a> <a href="#" class="btn btn-lg btn-light fw-bold border-white bg-white">Learn more</a>
</p> </p>
-->
</main> </main>
<!-- <!--
<footer class="mt-auto text-white-50"> <footer class="mt-auto text-white-50">

View file

@ -47,7 +47,10 @@ export class MiningComponent implements AfterViewInit {
this.navbarLinks = [ this.navbarLinks = [
new NavbarLink('pills-miner-data-tab', '#pills-miner-data', 'miner-data', true, 'Miner Data'), new NavbarLink('pills-miner-data-tab', '#pills-miner-data', 'miner-data', true, 'Miner Data'),
new NavbarLink('pills-alternate-chains-tab', '#pills-alternate-chains', 'alternate-chains', false, 'Alternate Chains'), new NavbarLink('pills-alternate-chains-tab', '#pills-alternate-chains', 'alternate-chains', false, 'Alternate Chains'),
new NavbarLink('pills-block-template-tab', '#pills-block-template', 'block-template', false, 'Block Template') new NavbarLink('pills-block-template-tab', '#pills-block-template', 'block-template', false, 'Block Template'),
new NavbarLink('pills-generate-blocks-tab', '#pills-generate-blocks', 'generate-blocks', false, 'Generate Blocks'),
new NavbarLink('pills-submit-block-tab', '#pills-submit-block', 'submit-block', false, 'Submit Block'),
new NavbarLink('pills-calc-pow-tab', '#pills-calc-pow', 'calc-pow', false, 'Calculate PoW Hash')
]; ];
this.router.events.subscribe((event) => { this.router.events.subscribe((event) => {

View file

@ -7,7 +7,7 @@
<ul class="nav nav-pills" id="pills-tab" role="tablist"> <ul class="nav nav-pills" id="pills-tab" role="tablist">
@for(navbarLink of navbarLinks; track navbarLink.name) { @for(navbarLink of navbarLinks; track navbarLink.name) {
<li class="nav-item mr-2" role="presentation"> <li class="nav-item mr-2" role="presentation">
<button [class]="navbarLink.selected ? 'nav-link active' : 'nav-link'" [id]="navbarLink.id" data-bs-toggle="pill" [attr.data-bs-target]="navbarLink.target" type="button" role="tab" [attr.aria-controls]="navbarLink.controls" [attr.aria-selected]="navbarLink.selected" [disabled]="navbarLink.disabled">{{navbarLink.name}}</button> <button [class]="navbarLink.selected ? 'nav-link active btn-sm' : 'nav-link btn-sm'" [id]="navbarLink.id" data-bs-toggle="pill" [attr.data-bs-target]="navbarLink.target" type="button" role="tab" [attr.aria-controls]="navbarLink.controls" [attr.aria-selected]="navbarLink.selected" [disabled]="navbarLink.disabled">{{navbarLink.name}}</button>
</li> </li>
} }
<!-- <!--

View file

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { OutputsComponent } from './outputs.component';
import { CommonModule } from '@angular/common';
const routes: Routes = [
{
path: 'outputs',
component: OutputsComponent
}
];
@NgModule({
imports: [CommonModule, RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class OutputsRoutingModule { }

View file

@ -0,0 +1 @@
<p>outputs works!</p>

View file

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OutputsComponent } from './outputs.component';
describe('OutputsComponent', () => {
let component: OutputsComponent;
let fixture: ComponentFixture<OutputsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OutputsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OutputsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,26 @@
import { AfterViewInit, Component } from '@angular/core';
import { NavbarLink } from '../navbar/navbar.model';
import { DaemonService } from '../core/services/daemon/daemon.service';
import { NavbarService } from '../navbar/navbar.service';
@Component({
selector: 'app-outputs',
templateUrl: './outputs.component.html',
styleUrl: './outputs.component.scss'
})
export class OutputsComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[];
constructor(private daemonService: DaemonService, private navbarService: NavbarService) {
this.navbarLinks = [
new NavbarLink('pills-outputs-overview-tab', '#pills-outputs-overview', 'outputs-overview', true, 'Overview'),
new NavbarLink('pills-outputs-histogram-tab', '#pills-outputs-histogram', 'outputs-histogram', false, 'Histogram'),
new NavbarLink('pills-outputs-distribution-tab', '#pills-outputs-distribution', 'outputs-distribution', false, 'Distribution')
];
}
ngAfterViewInit(): void {
this.navbarService.setNavbarLinks(this.navbarLinks);
}
}

View file

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OutputsRoutingModule } from './outputs-routing.module';
import { SharedModule } from '../shared/shared.module';
import { OutputsComponent } from './outputs.component';
@NgModule({
declarations: [OutputsComponent],
imports: [
CommonModule,
SharedModule,
OutputsRoutingModule
]
})
export class OutputsModule { }

View file

@ -1,3 +1,4 @@
<p> <div class="h-100 p-5 text-bg-dark rounded-3 m-4">
page-not-found works! <h2>Error</h2>
</p> <p>Page not found</p>
</div>

View file

@ -1,4 +1,4 @@
<div class="d-flex flex-column flex-shrink-0 p-3 text-bg-dark" style="width: 280px;"> <div class="d-flex flex-column flex-shrink-0 p-3 text-bg-dark" style="width: 200px;">
<ul class="nav nav-pills flex-column mb-auto"> <ul class="nav nav-pills flex-column mb-auto">
@for (navLink of navLinks; track navLink.title) { @for (navLink of navLinks; track navLink.title) {

View file

@ -17,6 +17,9 @@ export class SidebarComponent {
constructor(private router: Router) { constructor(private router: Router) {
this.navLinks = [ this.navLinks = [
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'), new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
new NavLink('Blockchain', '/blockchain', 'bi bi-bounding-box'),
new NavLink('Transactions', '/transactions', 'bi bi-credit-card-2-front'),
new NavLink('Outputs', '/outputs', 'bi bi-circle-fill'),
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'), new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'),
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'), new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'),
new NavLink('Bans', '/bans', 'bi bi-ban'), new NavLink('Bans', '/bans', 'bi bi-ban'),

View file

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TransactionsComponent } from './transactions.component';
import { CommonModule } from '@angular/common';
const routes: Routes = [
{
path: 'transactions',
component: TransactionsComponent
}
];
@NgModule({
imports: [CommonModule, RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TransactionsRoutingModule { }

View file

@ -0,0 +1 @@
<p>transactions works!</p>

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TransactionsComponent } from './transactions.component';
describe('TransactionsComponent', () => {
let component: TransactionsComponent;
let fixture: ComponentFixture<TransactionsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TransactionsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TransactionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,30 @@
import { AfterViewInit, Component } from '@angular/core';
import { DaemonService } from '../core/services/daemon/daemon.service';
import { NavbarService } from '../navbar/navbar.service';
import { NavbarLink } from '../navbar/navbar.model';
@Component({
selector: 'app-transactions',
templateUrl: './transactions.component.html',
styleUrl: './transactions.component.scss'
})
export class TransactionsComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[];
constructor(private daemonService: DaemonService, private navbarService: NavbarService) {
this.navbarLinks = [
new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', true, 'Relay Tx', true),
new NavbarLink('pills-tx-backlog', '#pills-tx-backlog', 'pills-tx-backlog', false, 'Tx Backlog', true),
new NavbarLink('pills-flush-tx-pool-tab', '#pills-flush-tx-pool', 'pills-flush-tx-pool', false, 'Flush Tx Pool', true),
new NavbarLink('pills-flush-cahe', '#pills-flush-cache', 'pills-flush-cache', false, 'Flush Cache', true)
];
}
ngAfterViewInit(): void {
this.navbarService.setNavbarLinks(this.navbarLinks);
}
private async load(): Promise<void> {
}
}

View file

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TransactionsRoutingModule } from './transactions-routing.module';
import { TransactionsComponent } from './transactions.component';
import { SharedModule } from '../shared/shared.module';
@NgModule({
declarations: [TransactionsComponent],
imports: [
CommonModule,
SharedModule,
TransactionsRoutingModule
]
})
export class TransactionsModule { }

View file

@ -0,0 +1,27 @@
import { RPCRequest } from "./RPCRequest";
export class GetTransactionsRequest extends RPCRequest {
public override readonly method: string = 'get_transactions';
public override readonly restricted: boolean = false;
public readonly txHashes: string[];
public readonly decodeAsJson: boolean;
public readonly prune: boolean;
public readonly split: boolean;
constructor(txHashes: string[], decodeAsJson: boolean, prune: boolean, split: boolean) {
super();
this.txHashes = txHashes;
this.decodeAsJson = decodeAsJson;
this.prune = prune;
this.split = split;
}
public toDictionary(): { [key: string]: any; } {
return {
"tx_hashes": this.txHashes,
"decodeAsJson": this.decodeAsJson,
"prune": this.prune,
"split": this.split
}
}
}