mirror of
https://github.com/mempool/mempool.git
synced 2025-06-04 12:13:12 +02:00
parent
61e8892204
commit
3e66e4d6db
@ -3,8 +3,8 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AssetsService } from '../../services/assets.service';
|
import { AssetsService } from '../../services/assets.service';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { Observable, of, Subject, zip, BehaviorSubject } from 'rxjs';
|
import { Observable, of, Subject, zip, BehaviorSubject, combineLatest } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, switchMap, catchError, map } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, switchMap, catchError, map, startWith, tap } from 'rxjs/operators';
|
||||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
@ -33,7 +33,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
|
|
||||||
@Output() searchTriggered = new EventEmitter();
|
@Output() searchTriggered = new EventEmitter();
|
||||||
@ViewChild('searchResults') searchResults: SearchResultsComponent;
|
@ViewChild('searchResults') searchResults: SearchResultsComponent;
|
||||||
@HostListener('keydown', ['$event']) keydown($event) {
|
@HostListener('keydown', ['$event']) keydown($event): void {
|
||||||
this.handleKeyDown($event);
|
this.handleKeyDown($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
private relativeUrlPipe: RelativeUrlPipe,
|
private relativeUrlPipe: RelativeUrlPipe,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit(): void {
|
||||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||||
|
|
||||||
this.searchForm = this.formBuilder.group({
|
this.searchForm = this.formBuilder.group({
|
||||||
@ -61,70 +61,111 @@ export class SearchFormComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.typeAhead$ = this.searchForm.get('searchText').valueChanges
|
const searchText$ = this.searchForm.get('searchText').valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
map((text) => {
|
map((text) => {
|
||||||
if (this.network === 'bisq' && text.match(/^(b)[^c]/i)) {
|
if (this.network === 'bisq' && text.match(/^(b)[^c]/i)) {
|
||||||
return text.substr(1);
|
return text.substr(1);
|
||||||
}
|
}
|
||||||
return text.trim();
|
return text.trim();
|
||||||
}),
|
}),
|
||||||
debounceTime(200),
|
distinctUntilChanged(),
|
||||||
distinctUntilChanged(),
|
);
|
||||||
switchMap((text) => {
|
|
||||||
if (!text.length) {
|
const searchResults$ = searchText$.pipe(
|
||||||
return of([
|
debounceTime(200),
|
||||||
'',
|
switchMap((text) => {
|
||||||
[],
|
if (!text.length) {
|
||||||
{
|
return of([
|
||||||
nodes: [],
|
[],
|
||||||
channels: [],
|
{ nodes: [], channels: [] }
|
||||||
}
|
]);
|
||||||
]);
|
}
|
||||||
}
|
this.isTypeaheading$.next(true);
|
||||||
this.isTypeaheading$.next(true);
|
if (!this.stateService.env.LIGHTNING) {
|
||||||
if (!this.stateService.env.LIGHTNING) {
|
|
||||||
return zip(
|
|
||||||
of(text),
|
|
||||||
this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
|
|
||||||
[{ nodes: [], channels: [] }],
|
|
||||||
of(this.regexBlockheight.test(text)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return zip(
|
return zip(
|
||||||
of(text),
|
|
||||||
this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
|
this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
|
||||||
this.apiService.lightningSearch$(text).pipe(catchError(() => of({
|
[{ nodes: [], channels: [] }],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return zip(
|
||||||
|
this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
|
||||||
|
this.apiService.lightningSearch$(text).pipe(catchError(() => of({
|
||||||
|
nodes: [],
|
||||||
|
channels: [],
|
||||||
|
}))),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
tap((result: any[]) => {
|
||||||
|
this.isTypeaheading$.next(false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.typeAhead$ = combineLatest(
|
||||||
|
[
|
||||||
|
searchText$,
|
||||||
|
searchResults$.pipe(
|
||||||
|
startWith([
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
nodes: [],
|
||||||
|
channels: [],
|
||||||
|
}
|
||||||
|
]))
|
||||||
|
]
|
||||||
|
).pipe(
|
||||||
|
map((latestData) => {
|
||||||
|
const searchText = latestData[0];
|
||||||
|
if (!searchText.length) {
|
||||||
|
return {
|
||||||
|
searchText: '',
|
||||||
|
hashQuickMatch: false,
|
||||||
|
blockHeight: false,
|
||||||
|
txId: false,
|
||||||
|
address: false,
|
||||||
|
addresses: [],
|
||||||
nodes: [],
|
nodes: [],
|
||||||
channels: [],
|
channels: [],
|
||||||
}))),
|
};
|
||||||
);
|
|
||||||
}),
|
|
||||||
map((result: any[]) => {
|
|
||||||
this.isTypeaheading$.next(false);
|
|
||||||
if (this.network === 'bisq') {
|
|
||||||
return result[0].map((address: string) => 'B' + address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const result = latestData[1];
|
||||||
|
const addressPrefixSearchResults = result[0];
|
||||||
|
const lightningResults = result[1];
|
||||||
|
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
return searchText.map((address: string) => 'B' + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesBlockHeight = this.regexBlockheight.test(searchText);
|
||||||
|
const matchesTxId = this.regexTransaction.test(searchText) && !this.regexBlockhash.test(searchText);
|
||||||
|
const matchesBlockHash = this.regexBlockhash.test(searchText);
|
||||||
|
const matchesAddress = this.regexAddress.test(searchText);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
searchText: result[0],
|
searchText: searchText,
|
||||||
blockHeight: this.regexBlockheight.test(result[0]) ? [parseInt(result[0], 10)] : [],
|
hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress),
|
||||||
addresses: result[1],
|
blockHeight: matchesBlockHeight,
|
||||||
nodes: result[2].nodes,
|
txId: matchesTxId,
|
||||||
channels: result[2].channels,
|
blockHash: matchesBlockHash,
|
||||||
totalResults: result[1].length + result[2].nodes.length + result[2].channels.length,
|
address: matchesAddress,
|
||||||
|
addresses: addressPrefixSearchResults,
|
||||||
|
nodes: lightningResults.nodes,
|
||||||
|
channels: lightningResults.channels,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handleKeyDown($event) {
|
|
||||||
|
handleKeyDown($event): void {
|
||||||
this.searchResults.handleKeyDown($event);
|
this.searchResults.handleKeyDown($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
itemSelected() {
|
itemSelected(): void {
|
||||||
setTimeout(() => this.search());
|
setTimeout(() => this.search());
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedResult(result: any) {
|
selectedResult(result: any): void {
|
||||||
if (typeof result === 'string') {
|
if (typeof result === 'string') {
|
||||||
this.search(result);
|
this.search(result);
|
||||||
} else if (typeof result === 'number') {
|
} else if (typeof result === 'number') {
|
||||||
@ -136,7 +177,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
search(result?: string) {
|
search(result?: string): void {
|
||||||
const searchText = result || this.searchForm.value.searchText.trim();
|
const searchText = result || this.searchForm.value.searchText.trim();
|
||||||
if (searchText) {
|
if (searchText) {
|
||||||
this.isSearching = true;
|
this.isSearching = true;
|
||||||
@ -170,7 +211,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(url: string, searchText: string, extras?: any) {
|
navigate(url: string, searchText: string, extras?: any): void {
|
||||||
this.router.navigate([this.relativeUrlPipe.transform(url), searchText], extras);
|
this.router.navigate([this.relativeUrlPipe.transform(url), searchText], extras);
|
||||||
this.searchTriggered.emit();
|
this.searchTriggered.emit();
|
||||||
this.searchForm.setValue({
|
this.searchForm.setValue({
|
||||||
|
@ -1,14 +1,32 @@
|
|||||||
<div class="dropdown-menu show" *ngIf="results" [hidden]="!results.blockHeight.length && !results.addresses.length && !results.nodes.length && !results.channels.length">
|
<div class="dropdown-menu show" *ngIf="results" [hidden]="!results.hashQuickMatch && !results.addresses.length && !results.nodes.length && !results.channels.length">
|
||||||
<ng-template [ngIf]="results.blockHeight.length">
|
<ng-template [ngIf]="results.blockHeight">
|
||||||
<div class="card-title">Bitcoin Block Height</div>
|
<div class="card-title">Bitcoin Block Height</div>
|
||||||
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
Go to "{{ results.searchText }}"
|
Go to "{{ results.searchText }}"
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="results.txId">
|
||||||
|
<div class="card-title">Bitcoin Transaction</div>
|
||||||
|
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
|
Go to "{{ results.searchText | shortenString : 13 }}"
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="results.address">
|
||||||
|
<div class="card-title">Bitcoin Address</div>
|
||||||
|
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
|
Go to "{{ results.searchText | shortenString : 13 }}"
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="results.blockHash">
|
||||||
|
<div class="card-title">Bitcoin Block</div>
|
||||||
|
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
|
Go to "{{ results.searchText | shortenString : 13 }}"
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
<ng-template [ngIf]="results.addresses.length">
|
<ng-template [ngIf]="results.addresses.length">
|
||||||
<div class="card-title" *ngIf="stateService.env.LIGHTNING">Bitcoin Addresses</div>
|
<div class="card-title" *ngIf="stateService.env.LIGHTNING">Bitcoin Addresses</div>
|
||||||
<ng-template ngFor [ngForOf]="results.addresses" let-address let-i="index">
|
<ng-template ngFor [ngForOf]="results.addresses" let-address let-i="index">
|
||||||
<button (click)="clickItem(results.blockHeight.length + i)" [class.active]="(results.blockHeight.length + i) === activeIdx" type="button" role="option" class="dropdown-item">
|
<button (click)="clickItem(results.hashQuickMatch + i)" [class.active]="(results.hashQuickMatch + i) === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
<ngb-highlight [result]="address | shortenString : isMobile ? 25 : 36" [term]="results.searchText"></ngb-highlight>
|
<ngb-highlight [result]="address | shortenString : isMobile ? 25 : 36" [term]="results.searchText"></ngb-highlight>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -16,7 +34,7 @@
|
|||||||
<ng-template [ngIf]="results.nodes.length">
|
<ng-template [ngIf]="results.nodes.length">
|
||||||
<div class="card-title">Lightning Nodes</div>
|
<div class="card-title">Lightning Nodes</div>
|
||||||
<ng-template ngFor [ngForOf]="results.nodes" let-node let-i="index">
|
<ng-template ngFor [ngForOf]="results.nodes" let-node let-i="index">
|
||||||
<button (click)="clickItem(results.blockHeight.length + results.addresses.length + i)" [class.inactive]="node.status === 0" [class.active]="results.blockHeight.length + results.addresses.length + i === activeIdx" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]" type="button" role="option" class="dropdown-item">
|
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + i)" [class.inactive]="node.status === 0" [class.active]="results.hashQuickMatch + results.addresses.length + i === activeIdx" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]" type="button" role="option" class="dropdown-item">
|
||||||
<ngb-highlight [result]="node.alias" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ node.public_key | shortenString : 10 }}</span>
|
<ngb-highlight [result]="node.alias" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ node.public_key | shortenString : 10 }}</span>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -24,7 +42,7 @@
|
|||||||
<ng-template [ngIf]="results.channels.length">
|
<ng-template [ngIf]="results.channels.length">
|
||||||
<div class="card-title">Lightning Channels</div>
|
<div class="card-title">Lightning Channels</div>
|
||||||
<ng-template ngFor [ngForOf]="results.channels" let-channel let-i="index">
|
<ng-template ngFor [ngForOf]="results.channels" let-channel let-i="index">
|
||||||
<button (click)="clickItem(results.blockHeight.length + results.addresses.length + results.nodes.length + i)" [class.inactive]="channel.status === 2" [class.active]="results.blockHeight.length + results.addresses.length + results.nodes.length + i === activeIdx" type="button" role="option" class="dropdown-item">
|
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + results.nodes.length + i)" [class.inactive]="channel.status === 2" [class.active]="results.hashQuickMatch + results.addresses.length + results.nodes.length + i === activeIdx" type="button" role="option" class="dropdown-item">
|
||||||
<ngb-highlight [result]="channel.short_id" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ channel.id }}</span>
|
<ngb-highlight [result]="channel.short_id" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ channel.id }}</span>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -22,7 +22,7 @@ export class SearchResultsComponent implements OnChanges {
|
|||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.activeIdx = 0;
|
this.activeIdx = 0;
|
||||||
if (this.results) {
|
if (this.results) {
|
||||||
this.resultsFlattened = [...this.results.blockHeight, ...this.results.addresses, ...this.results.nodes, ...this.results.channels];
|
this.resultsFlattened = [...(this.results.hashQuickMatch ? [this.results.searchText] : []), ...this.results.addresses, ...this.results.nodes, ...this.results.channels];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user