Bisq: Feature to filter transaction types.

This commit is contained in:
softsimon 2020-07-24 18:41:15 +07:00
parent 24182a6fb3
commit 7f71781916
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
8 changed files with 166 additions and 46 deletions

View File

@ -39,8 +39,12 @@ class Bisq {
return this.transactionIndex[txId];
}
getTransactions(start: number, length: number): [BisqTransaction[], number] {
return [this.transactions.slice(start, length + start), this.transactions.length];
getTransactions(start: number, length: number, types: string[]): [BisqTransaction[], number] {
let transactions = this.transactions;
if (types.length) {
transactions = transactions.filter((tx) => types.indexOf(tx.txType) > -1);
}
return [transactions.slice(start, length + start), transactions.length];
}
getBlock(hash: string): BisqBlock | undefined {

View File

@ -108,9 +108,21 @@ class Routes {
}
public getBisqTransactions(req: Request, res: Response) {
const types: string[] = [];
if (req.query.types && !Array.isArray(req.query.types)) {
res.status(500).send('Types is not an array');
return;
} else {
for (const _type in req.query.types) {
if (typeof req.query.types[_type] === 'string') {
types.push(req.query.types[_type].toString());
}
}
}
const index = parseInt(req.params.index, 10) || 0;
const length = parseInt(req.params.length, 10) > 100 ? 100 : parseInt(req.params.length, 10) || 25;
const [transactions, count] = bisq.getTransactions(index, length);
const [transactions, count] = bisq.getTransactions(index, length, types);
res.header('X-Total-Count', count.toString());
res.json(transactions);
}

View File

@ -1,9 +1,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgbButtonsModule, NgbPaginationModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { AppRoutingModule } from './app-routing.module';
@ -76,11 +74,7 @@ import { SharedModule } from './shared/shared.module';
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
BrowserAnimationsModule,
NgbButtonsModule,
NgbPaginationModule,
NgbDropdownModule,
InfiniteScrollModule,
SharedModule,
],

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { HttpClient, HttpResponse, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { BisqTransaction, BisqBlock, BisqStats } from './bisq.interfaces';
@ -23,8 +23,12 @@ export class BisqApiService {
return this.httpClient.get<BisqTransaction>(API_BASE_URL + '/tx/' + txId);
}
listTransactions$(start: number, length: number): Observable<HttpResponse<BisqTransaction[]>> {
return this.httpClient.get<BisqTransaction[]>(API_BASE_URL + `/txs/${start}/${length}`, { observe: 'response' });
listTransactions$(start: number, length: number, types: string[]): Observable<HttpResponse<BisqTransaction[]>> {
let params = new HttpParams();
types.forEach((t: string) => {
params = params.append('types[]', t);
});
return this.httpClient.get<BisqTransaction[]>(API_BASE_URL + `/txs/${start}/${length}`, { params, observe: 'response' });
}
getBlock$(hash: string): Observable<BisqBlock> {

View File

@ -1,38 +1,94 @@
<div class="container-xl">
<h1 style="float: left;">Transactions</h1>
<div ngbDropdown class="d-block float-right">
<button class="btn btn-primary" id="dropdownForm1" ngbDropdownToggle>Filter</button>
<div ngbDropdownMenu aria-labelledby="dropdownForm1">
<form [formGroup]="radioGroupForm">
<label>
<input type="checkbox" formControlName="GENESIS"> Genesis
</label>
<br>
<label>
<input type="checkbox" formControlName="TRANSFER_BSQ"> Transfer BSQ
</label>
<br>
<label>
<input type="checkbox" formControlName="PAY_TRADE_FEE"> Pay trade fee
</label>
<br>
<label>
<input type="checkbox" formControlName="PROPOSAL"> Proposal
</label>
<br>
<label>
<input type="checkbox" formControlName="COMPENSATION_REQUEST"> Compensation request
</label>
<br>
<label>
<input type="checkbox" formControlName="REIMBURSEMENT_REQUEST"> Reimbursement request
</label>
<br>
<label>
<input type="checkbox" formControlName="BLIND_VOTE"> Blind vote
</label>
<br>
<label>
<input type="checkbox" formControlName="VOTE_REVEAL"> Vote reveal
</label>
<br>
<label>
<input type="checkbox" formControlName="LOCKUP"> Lockup
</label>
<br>
<label>
<input type="checkbox" formControlName="UNLOCK"> Unlock
</label>
<br>
<label>
<input type="checkbox" formControlName="ASSET_LISTING_FEE"> Asset listing fee
</label>
<br>
<label>
<input type="checkbox" formControlName="PROOF_OF_BURN"> Proof of burn
</label>
</form>
</div>
</div>
<br>
<div class="clearfix"></div>
<table class="table table-borderless table-striped">
<thead>
<th style="width: 20%;">Transaction</th>
<th class="d-none d-md-block" style="width: 20%;">Type</th>
<th style="width: 20%;">Amount</th>
<th style="width: 20%;">Confirmed</th>
<th class="d-none d-md-block" style="width: 20%;">Height</th>
</thead>
<tbody *ngIf="!isLoading; else loadingTmpl">
<tr *ngFor="let tx of transactions; trackBy: trackByFn">
<td><a [routerLink]="['/tx/' | relativeUrl, tx.id]" [state]="{ data: tx }">{{ tx.id | slice : 0 : 8 }}</a></td>
<td class="d-none d-md-block">
<app-bisq-icon class="mr-1" [txType]="tx.txType"></app-bisq-icon>
<span class="d-none d-md-inline"> {{ tx.txTypeDisplayString }}</span>
</td>
<td>
<app-bisq-icon class="d-inline d-md-none mr-1" [txType]="tx.txType"></app-bisq-icon>
<ng-template [ngIf]="tx.txType === 'PAY_TRADE_FEE'" [ngIfElse]="defaultTxType">
{{ tx.burntFee / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span>
</ng-template>
<ng-template #defaultTxType>
{{ calculateTotalOutput(tx.outputs) / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span>
</ng-template>
</td>
<td><app-time-since [time]="tx.time / 1000" [fastRender]="true"></app-time-since> ago</td>
<td class="d-none d-md-block"><a [routerLink]="['/block/' | relativeUrl, tx.blockHash]" [state]="{ data: { blockHeight: tx.blockHeight } }">{{ tx.blockHeight }}</a></td>
</tr>
</tbody>
</table>
<table class="table table-borderless table-striped">
<thead>
<th style="width: 20%;">Transaction</th>
<th class="d-none d-md-block" style="width: 20%;">Type</th>
<th style="width: 20%;">Amount</th>
<th style="width: 20%;">Confirmed</th>
<th class="d-none d-md-block" style="width: 20%;">Height</th>
</thead>
<tbody *ngIf="!isLoading; else loadingTmpl">
<tr *ngFor="let tx of transactions; trackBy: trackByFn">
<td><a [routerLink]="['/tx/' | relativeUrl, tx.id]" [state]="{ data: tx }">{{ tx.id | slice : 0 : 8 }}</a></td>
<td class="d-none d-md-block">
<app-bisq-icon class="mr-1" [txType]="tx.txType"></app-bisq-icon>
<span class="d-none d-md-inline"> {{ tx.txTypeDisplayString }}</span>
</td>
<td>
<app-bisq-icon class="d-inline d-md-none mr-1" [txType]="tx.txType"></app-bisq-icon>
<ng-template [ngIf]="tx.txType === 'PAY_TRADE_FEE'" [ngIfElse]="defaultTxType">
{{ tx.burntFee / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span>
</ng-template>
<ng-template #defaultTxType>
{{ calculateTotalOutput(tx.outputs) / 100 | number: '1.2-2' }}<span class="d-none d-md-inline"> BSQ</span>
</ng-template>
</td>
<td><app-time-since [time]="tx.time / 1000" [fastRender]="true"></app-time-since> ago</td>
<td class="d-none d-md-block"><a [routerLink]="['/block/' | relativeUrl, tx.blockHash]" [state]="{ data: { blockHeight: tx.blockHeight } }">{{ tx.blockHeight }}</a></td>
</tr>
</tbody>
</table>
<br>

View File

@ -0,0 +1,4 @@
label {
padding: 0.25rem 1rem;
white-space: nowrap;
}

View File

@ -1,9 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { BisqTransaction, BisqOutput } from '../bisq.interfaces';
import { Subject } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { Subject, merge } from 'rxjs';
import { switchMap, tap, map } from 'rxjs/operators';
import { BisqApiService } from '../bisq-api.service';
import { SeoService } from 'src/app/services/seo.service';
import { FormGroup, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-bisq-transactions',
@ -20,6 +21,8 @@ export class BisqTransactionsComponent implements OnInit {
isLoading = true;
loadingItems: number[];
pageSubject$ = new Subject<number>();
radioGroupForm: FormGroup;
types: string[] = [];
// @ts-ignore
paginationSize: 'sm' | 'lg' = 'md';
@ -28,11 +31,29 @@ export class BisqTransactionsComponent implements OnInit {
constructor(
private bisqApiService: BisqApiService,
private seoService: SeoService,
private formBuilder: FormBuilder,
) { }
ngOnInit(): void {
this.seoService.setTitle('Transactions', true);
this.radioGroupForm = this.formBuilder.group({
UNVERIFIED: false,
INVALID: false,
GENESIS: false,
TRANSFER_BSQ: false,
PAY_TRADE_FEE: false,
PROPOSAL: false,
COMPENSATION_REQUEST: false,
REIMBURSEMENT_REQUEST: false,
BLIND_VOTE: false,
VOTE_REVEAL: false,
LOCKUP: false,
UNLOCK: false,
ASSET_LISTING_FEE: false,
PROOF_OF_BURN: false,
});
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
this.loadingItems = Array(this.itemsPerPage);
@ -41,10 +62,25 @@ export class BisqTransactionsComponent implements OnInit {
this.paginationMaxSize = 3;
}
this.pageSubject$
merge(
this.pageSubject$,
this.radioGroupForm.valueChanges
.pipe(
map((data) => {
const types: string[] = [];
for (const i in data) {
if (data[i]) {
types.push(i);
}
}
this.types = types;
return 1;
})
)
)
.pipe(
tap(() => this.isLoading = true),
switchMap((page) => this.bisqApiService.listTransactions$((page - 1) * this.itemsPerPage, this.itemsPerPage))
switchMap((page) => this.bisqApiService.listTransactions$((page - 1) * this.itemsPerPage, this.itemsPerPage, this.types))
)
.subscribe((response) => {
this.isLoading = false;

View File

@ -12,9 +12,11 @@ import { TimeSinceComponent } from '../components/time-since/time-since.componen
import { ClipboardComponent } from '../components/clipboard/clipboard.component';
import { QrcodeComponent } from '../components/qrcode/qrcode.component';
import { FiatComponent } from '../fiat/fiat.component';
import { NgbNavModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbNavModule, NgbTooltipModule, NgbButtonsModule, NgbPaginationModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { TxFeaturesComponent } from '../components/tx-features/tx-features.component';
import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.component';
import { ReactiveFormsModule } from '@angular/forms';
import { AngularMultiSelectModule } from 'angular2-multiselect-dropdown';
@NgModule({
declarations: [
@ -35,8 +37,12 @@ import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.
],
imports: [
CommonModule,
ReactiveFormsModule,
NgbNavModule,
NgbTooltipModule,
NgbButtonsModule,
NgbPaginationModule,
NgbDropdownModule,
],
providers: [
VbytesPipe,
@ -44,7 +50,11 @@ import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.
exports: [
NgbNavModule,
CommonModule,
ReactiveFormsModule,
NgbTooltipModule,
NgbButtonsModule,
NgbPaginationModule,
NgbDropdownModule,
TimeSinceComponent,
ClipboardComponent,
QrcodeComponent,