Asset search

This commit is contained in:
softsimon 2022-02-06 03:31:50 +04:00
parent 2e5c8bdfd3
commit d33c12cdee
No known key found for this signature in database
GPG key ID: 488D7DCFB5A430D7
4 changed files with 118 additions and 92 deletions

View file

@ -15,7 +15,7 @@
<form [formGroup]="searchForm" class="form-inline">
<div class="input-group mb-2">
<input style="width: 350px;" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearchFn" [resultFormatter]="formatterFn" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
<div class="input-group-append">
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off" i18n="Search Clear Button">Clear</button>
</div>

View file

@ -1,5 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
import { merge, Observable, of, Subject } from 'rxjs';
import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
import { AssetExtended } from 'src/app/interfaces/electrs.interface';
import { AssetsService } from 'src/app/services/assets.service';
import { SeoService } from 'src/app/services/seo.service';
import { StateService } from 'src/app/services/state.service';
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-assets-nav',
@ -7,16 +17,78 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
styleUrls: ['./assets-nav.component.scss']
})
export class AssetsNavComponent implements OnInit {
activeTab = 0;
@ViewChild('instance', {static: true}) instance: NgbTypeahead;
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
searchForm: FormGroup;
assetsCache: AssetExtended[];
typeaheadSearchFn: ((text: Observable<string>) => Observable<readonly any[]>);
formatterFn = (asset: AssetExtended) => asset.name + ' (' + asset.ticker + ')';
focus$ = new Subject<string>();
click$ = new Subject<string>();
itemsPerPage = 15;
constructor(
private formBuilder: FormBuilder,
private seoService: SeoService,
private router: Router,
private assetsService: AssetsService,
private stateService: StateService,
private relativeUrlPipe: RelativeUrlPipe,
) { }
ngOnInit(): void {
this.typeaheadSearchFn = this.typeaheadSearch;
this.searchForm = this.formBuilder.group({
searchText: [{ value: '', disabled: true }, Validators.required]
searchText: [{ value: '', disabled: false }, Validators.required]
});
}
typeaheadSearch = (text$: Observable<string>) => {
const debouncedText$ = text$.pipe(
distinctUntilChanged()
);
const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
const inputFocus$ = this.focus$;
return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$)
.pipe(
switchMap((searchText) => {
if (!searchText.length) {
return of([]);
}
return this.assetsService.getAssetsJson$.pipe(
map((assets) => {
if (searchText.length ) {
const filteredAssets = assets.filter((asset) => asset.name.toLowerCase().indexOf(searchText.toLowerCase()) > -1
|| (asset.ticker || '').toLowerCase().indexOf(searchText.toLowerCase()) > -1
|| (asset.entity && asset.entity.domain || '').toLowerCase().indexOf(searchText.toLowerCase()) > -1);
assets = filteredAssets;
return filteredAssets.slice(0, this.itemsPerPage);
} else {
return assets.slice(0, this.itemsPerPage);
}
})
)
}),
);
}
itemSelected() {
setTimeout(() => this.search());
}
search() {
const searchText = this.searchForm.value.searchText;
this.navigate('/assets/asset/', searchText.asset_id);
}
navigate(url: string, searchText: string, extras?: any) {
this.router.navigate([this.relativeUrlPipe.transform(url), searchText], extras);
this.searchForm.setValue({
searchText: '',
});
}

View file

@ -1,10 +1,10 @@
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { AssetsService } from 'src/app/services/assets.service';
import { environment } from 'src/environments/environment';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { distinctUntilChanged, map, filter, mergeMap, tap, take } from 'rxjs/operators';
import { FormGroup } from '@angular/forms';
import { filter, map, switchMap, take } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { merge, combineLatest, Observable } from 'rxjs';
import { combineLatest, Observable } from 'rxjs';
import { AssetExtended } from 'src/app/interfaces/electrs.interface';
import { SeoService } from 'src/app/services/seo.service';
import { StateService } from 'src/app/services/state.service';
@ -32,7 +32,6 @@ export class AssetsComponent implements OnInit {
constructor(
private assetsService: AssetsService,
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private seoService: SeoService,
@ -43,56 +42,20 @@ export class AssetsComponent implements OnInit {
this.seoService.setTitle($localize`:@@ee8f8008bae6ce3a49840c4e1d39b4af23d4c263:Assets`);
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
this.searchForm = this.formBuilder.group({
searchText: [{ value: '', disabled: true }, Validators.required]
});
this.assets$ = combineLatest([
this.assetsService.getAssetsJson$,
this.route.queryParams,
])
.pipe(
take(1),
mergeMap(([assets, qp]) => {
this.assets = Object.values(assets);
if (this.stateService.network === 'liquid') {
// @ts-ignore
this.assets.push({
name: 'Liquid Bitcoin',
ticker: 'L-BTC',
asset_id: this.nativeAssetId,
});
} else if (this.stateService.network === 'liquidtestnet') {
// @ts-ignore
this.assets.push({
name: 'Test Liquid Bitcoin',
ticker: 'tL-BTC',
asset_id: this.nativeAssetId,
});
}
.pipe(
take(1),
switchMap(([assets, qp]) => {
this.assets = assets;
this.assets = this.assets.sort((a: any, b: any) => a.name.localeCompare(b.name));
this.assetsCache = this.assets;
this.searchForm.get('searchText').enable();
if (qp.search) {
this.searchForm.get('searchText').setValue(qp.search, { emitEvent: false });
}
return merge(
this.searchForm.get('searchText').valueChanges
.pipe(
distinctUntilChanged(),
tap((text) => {
this.page = 1;
this.searchTextChanged(text);
})
),
this.route.queryParams
return this.route.queryParams
.pipe(
filter((queryParams) => {
const newPage = parseInt(queryParams.page, 10);
if (newPage !== this.page || queryParams.search !== this.searchForm.get('searchText').value) {
if (newPage !== this.page) {
return true;
}
return false;
@ -104,38 +67,19 @@ export class AssetsComponent implements OnInit {
} else {
this.page = 1;
}
if (this.searchForm.get('searchText').value !== (queryParams.search || '')) {
this.searchTextChanged(queryParams.search);
}
if (queryParams.search) {
this.searchForm.get('searchText').setValue(queryParams.search, { emitEvent: false });
return queryParams.search;
}
return '';
})
),
);
}),
map((searchText) => {
const start = (this.page - 1) * this.itemsPerPage;
if (searchText.length ) {
const filteredAssets = this.assetsCache.filter((asset) => asset.name.toLowerCase().indexOf(searchText.toLowerCase()) > -1
|| (asset.ticker || '').toLowerCase().indexOf(searchText.toLowerCase()) > -1);
this.assets = filteredAssets;
return filteredAssets.slice(start, this.itemsPerPage + start);
} else {
this.assets = this.assetsCache;
);
}),
map(() => {
const start = (this.page - 1) * this.itemsPerPage;
return this.assets.slice(start, this.itemsPerPage + start);
}
})
);
})
);
}
pageChange(page: number) {
const queryParams = { page: page, search: this.searchForm.get('searchText').value };
if (queryParams.search === '') {
queryParams.search = null;
}
const queryParams = { page: page };
if (queryParams.page === 1) {
queryParams.page = null;
}
@ -147,21 +91,6 @@ export class AssetsComponent implements OnInit {
});
}
searchTextChanged(text: string) {
const queryParams = { search: text, page: 1 };
if (queryParams.search === '') {
queryParams.search = null;
}
if (queryParams.page === 1) {
queryParams.page = null;
}
this.router.navigate([], {
relativeTo: this.route,
queryParams: queryParams,
queryParamsHandling: 'merge',
});
}
trackByAsset(index: number, asset: any) {
return asset.asset_id;
}

View file

@ -3,12 +3,16 @@ import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { StateService } from './state.service';
import { environment } from 'src/environments/environment';
import { AssetExtended } from '../interfaces/electrs.interface';
@Injectable({
providedIn: 'root'
})
export class AssetsService {
getAssetsJson$: Observable<any>;
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
getAssetsJson$: Observable<AssetExtended[]>;
getAssetsMinimalJson$: Observable<any>;
getMiningPools$: Observable<any>;
@ -24,6 +28,27 @@ export class AssetsService {
this.getAssetsJson$ = this.stateService.networkChanged$
.pipe(
switchMap(() => this.httpClient.get(`${apiBaseUrl}/resources/assets${this.stateService.network === 'liquidtestnet' ? '-testnet' : ''}.json`)),
map((rawAssets) => {
const assets: AssetExtended[] = Object.values(rawAssets);
if (this.stateService.network === 'liquid') {
// @ts-ignore
assets.push({
name: 'Liquid Bitcoin',
ticker: 'L-BTC',
asset_id: this.nativeAssetId,
});
} else if (this.stateService.network === 'liquidtestnet') {
// @ts-ignore
assets.push({
name: 'Test Liquid Bitcoin',
ticker: 'tL-BTC',
asset_id: this.nativeAssetId,
});
}
return assets.sort((a: any, b: any) => a.name.localeCompare(b.name));
}),
shareReplay(1),
);
this.getAssetsMinimalJson$ = this.stateService.networkChanged$