Address index and api.
Address view.
This commit is contained in:
@@ -22,7 +22,6 @@ import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.
|
||||
import { WebsocketService } from './services/websocket.service';
|
||||
import { AddressLabelsComponent } from './components/address-labels/address-labels.component';
|
||||
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
||||
import { QrcodeComponent } from './components/qrcode/qrcode.component';
|
||||
import { MasterPageComponent } from './components/master-page/master-page.component';
|
||||
import { AboutComponent } from './components/about/about.component';
|
||||
import { TelevisionComponent } from './components/television/television.component';
|
||||
@@ -64,7 +63,6 @@ import { SharedModule } from './shared/shared.module';
|
||||
TimespanComponent,
|
||||
AddressLabelsComponent,
|
||||
MempoolBlocksComponent,
|
||||
QrcodeComponent,
|
||||
ChartistComponent,
|
||||
FooterComponent,
|
||||
FiatComponent,
|
||||
|
||||
106
frontend/src/app/bisq/bisq-address/bisq-address.component.html
Normal file
106
frontend/src/app/bisq/bisq-address/bisq-address.component.html
Normal file
@@ -0,0 +1,106 @@
|
||||
<div class="container-xl">
|
||||
<h1 style="float: left;">Bisq Address</h1>
|
||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" style="line-height: 56px; margin-left: 10px;">
|
||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="addressString"></app-clipboard>
|
||||
<br>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<ng-template [ngIf]="!isLoadingAddress && !error">
|
||||
<div class="box">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Total received</td>
|
||||
<td>{{ totalReceived / 100 }} BSQ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total sent</td>
|
||||
<td>{{ totalSent / 100 }} BSQ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Final balance</td>
|
||||
<td>{{ (totalReceived - totalSent) / 100 }} BSQ</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col qrcode-col">
|
||||
<div class="qr-wrapper">
|
||||
<app-qrcode [data]="addressString"></app-qrcode>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>{{ transactions.length | number }} transactions</h2>
|
||||
|
||||
<ng-template ngFor let-tx [ngForOf]="transactions">
|
||||
|
||||
<div class="header-bg box" style="padding: 10px; margin-bottom: 10px;">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, tx.id]" [state]="{ data: tx }">
|
||||
<span style="float: left;" class="d-block d-md-none">{{ tx.id | shortenString : 16 }}</span>
|
||||
<span style="float: left;" class="d-none d-md-block">{{ tx.id }}</span>
|
||||
</a>
|
||||
<div class="float-right">
|
||||
{{ tx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<app-bisq-transfers [tx]="tx"></app-bisq-transfers>
|
||||
|
||||
<br>
|
||||
</ng-template>
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="isLoadingAddress && !error">
|
||||
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<div class="text-center">
|
||||
Error loading address data.
|
||||
<br>
|
||||
<i>{{ error.error }}</i>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -0,0 +1,23 @@
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.qrcode-col {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.qrcode-col {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qrcode-col > div {
|
||||
margin-top: 20px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
82
frontend/src/app/bisq/bisq-address/bisq-address.component.ts
Normal file
82
frontend/src/app/bisq/bisq-address/bisq-address.component.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { switchMap, filter, catchError } from 'rxjs/operators';
|
||||
import { ParamMap, ActivatedRoute } from '@angular/router';
|
||||
import { Subscription, of } from 'rxjs';
|
||||
import { BisqTransaction } from '../bisq.interfaces';
|
||||
import { BisqApiService } from '../bisq-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-address',
|
||||
templateUrl: './bisq-address.component.html',
|
||||
styleUrls: ['./bisq-address.component.scss']
|
||||
})
|
||||
export class BisqAddressComponent implements OnInit, OnDestroy {
|
||||
transactions: BisqTransaction[];
|
||||
addressString: string;
|
||||
isLoadingAddress = true;
|
||||
error: any;
|
||||
mainSubscription: Subscription;
|
||||
|
||||
totalReceived = 0;
|
||||
totalSent = 0;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private seoService: SeoService,
|
||||
private bisqApiService: BisqApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.mainSubscription = this.route.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
this.error = undefined;
|
||||
this.isLoadingAddress = true;
|
||||
this.transactions = null;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.addressString = params.get('id') || '';
|
||||
this.seoService.setTitle('Address: ' + this.addressString, true);
|
||||
|
||||
return this.bisqApiService.getAddress$(this.addressString)
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
this.isLoadingAddress = false;
|
||||
this.error = err;
|
||||
console.log(err);
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
}),
|
||||
filter((transactions) => transactions !== null)
|
||||
)
|
||||
.subscribe((transactions: BisqTransaction[]) => {
|
||||
this.transactions = transactions;
|
||||
this.updateChainStats();
|
||||
this.isLoadingAddress = false;
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
this.error = error;
|
||||
this.isLoadingAddress = false;
|
||||
});
|
||||
}
|
||||
|
||||
updateChainStats() {
|
||||
const shortenedAddress = this.addressString.substr(1);
|
||||
|
||||
this.totalSent = this.transactions.reduce((acc, tx) =>
|
||||
acc + tx.inputs
|
||||
.filter((input) => input.address === shortenedAddress)
|
||||
.reduce((a, input) => a + input.bsqAmount, 0), 0);
|
||||
|
||||
this.totalReceived = this.transactions.reduce((acc, tx) =>
|
||||
acc + tx.outputs
|
||||
.filter((output) => output.address === shortenedAddress)
|
||||
.reduce((a, output) => a + output.bsqAmount, 0), 0);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.mainSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,8 @@ export class BisqApiService {
|
||||
listBlocks$(start: number, length: number): Observable<HttpResponse<BisqBlock[]>> {
|
||||
return this.httpClient.get<BisqBlock[]>(API_BASE_URL + `/bisq/blocks/${start}/${length}`, { observe: 'response' });
|
||||
}
|
||||
|
||||
getAddress$(address: string): Observable<BisqTransaction[]> {
|
||||
return this.httpClient.get<BisqTransaction[]>(API_BASE_URL + '/bisq/address/' + address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="title-block">
|
||||
<h1>Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
<h1>Bisq Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BisqBlockComponent } from './bisq-block.component';
|
||||
|
||||
describe('BisqBlockComponent', () => {
|
||||
let component: BisqBlockComponent;
|
||||
let fixture: ComponentFixture<BisqBlockComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ BisqBlockComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BisqBlockComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import { BisqApiService } from '../bisq-api.service';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Subscribable, Subscription, of } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-block',
|
||||
@@ -21,6 +22,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private bisqApiService: BisqApiService,
|
||||
private route: ActivatedRoute,
|
||||
private seoService: SeoService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -28,6 +30,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
this.blockHash = params.get('id') || '';
|
||||
document.body.scrollTo(0, 0);
|
||||
this.isLoading = true;
|
||||
if (history.state.data && history.state.data.blockHeight) {
|
||||
this.blockHeight = history.state.data.blockHeight;
|
||||
@@ -42,6 +45,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
|
||||
.subscribe((block: BisqBlock) => {
|
||||
this.isLoading = false;
|
||||
this.blockHeight = block.height;
|
||||
this.seoService.setTitle('Block: #' + block.height + ': ' + block.hash, true);
|
||||
this.block = block;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container-xl">
|
||||
<h2 style="float: left;">BSQ Blocks</h2>
|
||||
<h2 style="float: left;">Bisq Blocks</h2>
|
||||
<br>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BisqApiService } from '../bisq-api.service';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BisqBlock, BisqOutput, BisqTransaction } from '../bisq.interfaces';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-blocks',
|
||||
@@ -21,9 +22,11 @@ export class BisqBlocksComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private bisqApiService: BisqApiService,
|
||||
private seoService: SeoService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seoService.setTitle('Blocks', true);
|
||||
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
|
||||
|
||||
this.pageSubject$
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<h1 class="float-left mr-3 mb-md-3">Transaction</h1>
|
||||
<h1 class="float-left mr-3 mb-md-3">Bisq Transaction</h1>
|
||||
|
||||
<ng-template [ngIf]="!isLoading" [ngIfElse]="isLoadingTmpl">
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { of, Observable, Subscription } from 'rxjs';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Block } from 'src/app/interfaces/electrs.interface';
|
||||
import { BisqApiService } from '../bisq-api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-transaction',
|
||||
@@ -23,13 +24,16 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
|
||||
private route: ActivatedRoute,
|
||||
private bisqApiService: BisqApiService,
|
||||
private stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscription = this.route.paramMap.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
this.isLoading = true;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.txId = params.get('id') || '';
|
||||
this.seoService.setTitle('Transaction: ' + this.txId, true);
|
||||
if (history.state.data) {
|
||||
return of(history.state.data);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container-xl">
|
||||
<h2 style="float: left;">BSQ Transactions</h2>
|
||||
<h2 style="float: left;">Bisq Transactions</h2>
|
||||
<br>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BisqTransaction, BisqOutput } from '../bisq.interfaces';
|
||||
import { Subject } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { BisqApiService } from '../bisq-api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-transactions',
|
||||
@@ -21,9 +22,12 @@ export class BisqTransactionsComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private bisqApiService: BisqApiService,
|
||||
private seoService: SeoService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seoService.setTitle('Transactions', true);
|
||||
|
||||
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
|
||||
|
||||
this.pageSubject$
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</ng-template>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/address/' | relativeUrl, input.address]" title="{{ input.address }}">
|
||||
<a [routerLink]="['/address/' | relativeUrl, 'B' + input.address]" title="B{{ input.address }}">
|
||||
<span class="d-block d-lg-none">B{{ input.address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">B{{ input.address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
@@ -36,7 +36,7 @@
|
||||
<ng-template ngFor let-output [ngForOf]="tx.outputs" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr *ngIf="output.isVerified && output.opReturn === undefined">
|
||||
<td>
|
||||
<a [routerLink]="['/address/' | relativeUrl, output.address]" title="{{ output.address }}">
|
||||
<a [routerLink]="['/address/' | relativeUrl, 'B' + output.address]" title="B{{ output.address }}">
|
||||
<span class="d-block d-lg-none">B{{ output.address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">B{{ output.address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { faLeaf, faQuestion, faExclamationTriangle, faRocket, faRetweet, faFileA
|
||||
import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component';
|
||||
import { BisqExplorerComponent } from './bisq-explorer/bisq-explorer.component';
|
||||
import { BisqApiService } from './bisq-api.service';
|
||||
import { BisqAddressComponent } from './bisq-address/bisq-address.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -27,6 +28,7 @@ import { BisqApiService } from './bisq-api.service';
|
||||
BisqTransfersComponent,
|
||||
BisqBlocksComponent,
|
||||
BisqExplorerComponent,
|
||||
BisqAddressComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.co
|
||||
import { BisqBlockComponent } from './bisq-block/bisq-block.component';
|
||||
import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component';
|
||||
import { BisqExplorerComponent } from './bisq-explorer/bisq-explorer.component';
|
||||
import { BisqAddressComponent } from './bisq-address/bisq-address.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -32,7 +33,7 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'address/:id',
|
||||
component: AddressComponent
|
||||
component: BisqAddressComponent,
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
|
||||
@@ -18,14 +18,9 @@ export class SeoService {
|
||||
|
||||
setTitle(newTitle: string, prependNetwork = false) {
|
||||
let networkName = '';
|
||||
if (prependNetwork) {
|
||||
if (this.network === 'liquid') {
|
||||
networkName = 'Liquid ';
|
||||
} else if (this.network === 'testnet') {
|
||||
networkName = 'Testnet ';
|
||||
}
|
||||
if (prependNetwork && this.network !== '') {
|
||||
networkName = this.network.substr(0, 1).toUpperCase() + this.network.substr(1) + ' ';
|
||||
}
|
||||
|
||||
this.titleService.setTitle(networkName + newTitle + ' - ' + this.defaultTitle);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BytesPipe } from './pipes/bytes-pipe/bytes.pipe';
|
||||
import { WuBytesPipe } from './pipes/bytes-pipe/wubytes.pipe';
|
||||
import { TimeSinceComponent } from '../components/time-since/time-since.component';
|
||||
import { ClipboardComponent } from '../components/clipboard/clipboard.component';
|
||||
import { QrcodeComponent } from '../components/qrcode/qrcode.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -23,6 +24,7 @@ import { ClipboardComponent } from '../components/clipboard/clipboard.component'
|
||||
ShortenStringPipe,
|
||||
ClipboardComponent,
|
||||
TimeSinceComponent,
|
||||
QrcodeComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -40,7 +42,8 @@ import { ClipboardComponent } from '../components/clipboard/clipboard.component'
|
||||
CeilPipe,
|
||||
ShortenStringPipe,
|
||||
TimeSinceComponent,
|
||||
ClipboardComponent
|
||||
ClipboardComponent,
|
||||
QrcodeComponent,
|
||||
]
|
||||
})
|
||||
export class SharedModule {}
|
||||
|
||||
Reference in New Issue
Block a user