Merge pull request #517 from mempool/simon/bisq-address-prefix-search
Handle the 'B' prefix in the search bar autocomplete on /bisq
This commit is contained in:
		
						commit
						3ffa60db1f
					
				@ -1,7 +1,7 @@
 | 
				
			|||||||
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
 | 
					<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
 | 
				
			||||||
  <div class="d-flex">
 | 
					  <div class="d-flex">
 | 
				
			||||||
    <div class="search-box-container mr-2">
 | 
					    <div class="search-box-container mr-2">
 | 
				
			||||||
      <input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (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-form.searchbar-placeholder" placeholder="TXID, block height, hash or address">
 | 
					      <input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearchFn" (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-form.searchbar-placeholder" placeholder="TXID, block height, hash or address">
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" i18n-title="search-form.search-title" title="Search"></fa-icon></button>
 | 
					      <button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" i18n-title="search-form.search-title" title="Search"></fa-icon></button>
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import { Router } from '@angular/router';
 | 
				
			|||||||
import { AssetsService } from 'src/app/services/assets.service';
 | 
					import { AssetsService } from 'src/app/services/assets.service';
 | 
				
			||||||
import { StateService } from 'src/app/services/state.service';
 | 
					import { StateService } from 'src/app/services/state.service';
 | 
				
			||||||
import { Observable, of, Subject, merge } from 'rxjs';
 | 
					import { Observable, of, Subject, merge } from 'rxjs';
 | 
				
			||||||
import { debounceTime, distinctUntilChanged, switchMap, filter, catchError } from 'rxjs/operators';
 | 
					import { debounceTime, distinctUntilChanged, switchMap, filter, catchError, map } from 'rxjs/operators';
 | 
				
			||||||
import { ElectrsApiService } from 'src/app/services/electrs-api.service';
 | 
					import { ElectrsApiService } from 'src/app/services/electrs-api.service';
 | 
				
			||||||
import { NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
 | 
					import { NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,11 +18,12 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
  network = '';
 | 
					  network = '';
 | 
				
			||||||
  assets: object = {};
 | 
					  assets: object = {};
 | 
				
			||||||
  isSearching = false;
 | 
					  isSearching = false;
 | 
				
			||||||
 | 
					  typeaheadSearchFn: ((text: Observable<string>) => Observable<readonly any[]>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  searchForm: FormGroup;
 | 
					  searchForm: FormGroup;
 | 
				
			||||||
  @Output() searchTriggered = new EventEmitter();
 | 
					  @Output() searchTriggered = new EventEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,87})$/;
 | 
					  regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[bB]?[a-z]{2,5}1[ac-hj-np-z02-9]{8,87})$/;
 | 
				
			||||||
  regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
 | 
					  regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
 | 
				
			||||||
  regexTransaction = /^[a-fA-F0-9]{64}$/;
 | 
					  regexTransaction = /^[a-fA-F0-9]{64}$/;
 | 
				
			||||||
  regexBlockheight = /^[0-9]+$/;
 | 
					  regexBlockheight = /^[0-9]+$/;
 | 
				
			||||||
@ -31,21 +32,6 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
  focus$ = new Subject<string>();
 | 
					  focus$ = new Subject<string>();
 | 
				
			||||||
  click$ = new Subject<string>();
 | 
					  click$ = new Subject<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  typeaheadSearch = (text$: Observable<string>) => {
 | 
					 | 
				
			||||||
    const debouncedText$ = text$.pipe(debounceTime(200), distinctUntilChanged());
 | 
					 | 
				
			||||||
    const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
 | 
					 | 
				
			||||||
    const inputFocus$ = this.focus$;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$)
 | 
					 | 
				
			||||||
      .pipe(
 | 
					 | 
				
			||||||
        switchMap((text) => {
 | 
					 | 
				
			||||||
          if (!text.length) { return of([]); }
 | 
					 | 
				
			||||||
          return this.electrsApiService.getAddressesByPrefix$(text)
 | 
					 | 
				
			||||||
            .pipe(catchError(() => of([])));
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private formBuilder: FormBuilder,
 | 
					    private formBuilder: FormBuilder,
 | 
				
			||||||
    private router: Router,
 | 
					    private router: Router,
 | 
				
			||||||
@ -55,11 +41,13 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
 | 
					    this.typeaheadSearchFn = this.typeaheadSearch;
 | 
				
			||||||
    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({
 | 
				
			||||||
      searchText: ['', Validators.required],
 | 
					      searchText: ['', Validators.required],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.network === 'liquid') {
 | 
					    if (this.network === 'liquid') {
 | 
				
			||||||
      this.assetsService.getAssetsMinimalJson$
 | 
					      this.assetsService.getAssetsMinimalJson$
 | 
				
			||||||
        .subscribe((assets) => {
 | 
					        .subscribe((assets) => {
 | 
				
			||||||
@ -68,6 +56,37 @@ export class SearchFormComponent implements OnInit {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  typeaheadSearch = (text$: Observable<string>) => {
 | 
				
			||||||
 | 
					    const debouncedText$ = text$.pipe(
 | 
				
			||||||
 | 
					      map((text) => {
 | 
				
			||||||
 | 
					        if (this.network === 'bisq' && text.match(/^(b)[^c]/i)) {
 | 
				
			||||||
 | 
					          return text.substr(1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return text;
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      debounceTime(200),
 | 
				
			||||||
 | 
					      distinctUntilChanged()
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
 | 
				
			||||||
 | 
					    const inputFocus$ = this.focus$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$)
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap((text) => {
 | 
				
			||||||
 | 
					          if (!text.length) {
 | 
				
			||||||
 | 
					            return of([]);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([])));
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        map((result: string[]) => {
 | 
				
			||||||
 | 
					          if (this.network === 'bisq') {
 | 
				
			||||||
 | 
					            return result.map((address: string) => 'B' + address);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return result;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  itemSelected() {
 | 
					  itemSelected() {
 | 
				
			||||||
    setTimeout(() => this.search());
 | 
					    setTimeout(() => this.search());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user