use new esplora registry search for main search form

This commit is contained in:
mononaut 2026-05-25 17:31:19 +00:00
parent 28d202c28b
commit c144f0358f
No known key found for this signature in database
GPG key ID: BFD16BE592A9CD8D
4 changed files with 63 additions and 35 deletions

View file

@ -22,7 +22,6 @@ export class SearchFormComponent implements OnInit {
@Input() hamburgerOpen = false;
env: Env;
network = '';
assets: object = {};
pools: object[] = [];
isSearching = false;
isTypeaheading$ = new BehaviorSubject<boolean>(false);
@ -96,13 +95,6 @@ export class SearchFormComponent implements OnInit {
searchText: ['', Validators.required],
});
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
this.assetsService.getAssetsMinimalJson$
.subscribe((assets) => {
this.assets = assets;
});
}
const searchText$ = this.searchForm.get('searchText').valueChanges
.pipe(
map((text) => {
@ -121,7 +113,8 @@ export class SearchFormComponent implements OnInit {
return of([
[],
{ nodes: [], channels: [] },
this.pools
this.pools,
[],
]);
}
this.isTypeaheading$.next(true);
@ -129,7 +122,8 @@ export class SearchFormComponent implements OnInit {
return zip(
this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
[{ nodes: [], channels: [] }],
this.getMiningPools()
this.getMiningPools(),
this.getLiquidAssetSearch$(text),
);
}
return zip(
@ -138,7 +132,8 @@ export class SearchFormComponent implements OnInit {
nodes: [],
channels: [],
}))),
this.getMiningPools()
this.getMiningPools(),
this.getLiquidAssetSearch$(text),
);
}),
map((result: any[]) => {
@ -159,7 +154,8 @@ export class SearchFormComponent implements OnInit {
nodes: [],
channels: [],
},
this.pools
this.pools,
[],
]))
]
).pipe(
@ -178,7 +174,7 @@ export class SearchFormComponent implements OnInit {
addresses: [],
nodes: [],
channels: [],
liquidAsset: [],
liquidAssets: [],
pools: []
};
}
@ -186,6 +182,7 @@ export class SearchFormComponent implements OnInit {
const result = latestData[1];
const addressPrefixSearchResults = result[0];
const lightningResults = result[1];
const liquidAssets = result[3];
// Do not show date and timestamp results for liquid
const isNetworkBitcoin = this.network === '' || this.network === 'testnet' || this.network === 'testnet4' || this.network === 'signet';
@ -198,8 +195,8 @@ export class SearchFormComponent implements OnInit {
const matchesAddress = !matchesTxId && this.regexAddress.test(searchText);
const publicKey = matchesAddress && searchText.startsWith('0');
const otherNetworks = findOtherNetworks(searchText, this.network as any || 'mainnet', this.env);
const liquidAsset = this.assets ? (this.assets[searchText] || []) : [];
const pools = this.pools.filter(pool => pool['name'].toLowerCase().includes(searchText.toLowerCase())).slice(0, 10);
const hashQuickMatch = +(matchesBlockHeight || matchesBlockHash || (matchesTxId && !liquidAssets.length) || matchesAddress || matchesUnixTimestamp || matchesDateTime);
if (matchesDateTime && searchText.indexOf('/') !== -1) {
searchText = searchText.replace(/\//g, '-');
@ -211,7 +208,7 @@ export class SearchFormComponent implements OnInit {
return {
searchText: searchText,
hashQuickMatch: +(matchesBlockHeight || matchesBlockHash || matchesTxId || matchesAddress || matchesUnixTimestamp || matchesDateTime),
hashQuickMatch: hashQuickMatch,
blockHeight: matchesBlockHeight,
dateTime: matchesDateTime,
unixTimestamp: matchesUnixTimestamp,
@ -223,7 +220,7 @@ export class SearchFormComponent implements OnInit {
otherNetworks: otherNetworks,
nodes: lightningResults.nodes,
channels: lightningResults.channels,
liquidAsset: liquidAsset,
liquidAssets: liquidAssets,
pools: pools
};
})
@ -258,6 +255,8 @@ export class SearchFormComponent implements OnInit {
}
} else if (result.slug) {
this.navigate('/mining/pool/', result.slug);
} else if (result.asset_id) {
this.navigate('/assets/asset/', result.asset_id);
}
}
@ -275,19 +274,24 @@ export class SearchFormComponent implements OnInit {
} else if (this.regexTransaction.test(searchText)) {
const matches = this.regexTransaction.exec(searchText);
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
if (this.assets[matches[0]]) {
this.navigate('/assets/asset/', matches[0]);
}
this.electrsApiService.getAsset$(matches[0])
.subscribe(
() => { this.navigate('/assets/asset/', matches[0]); },
() => {
this.electrsApiService.getBlock$(matches[0])
this.assetsService.searchLiquidAssets$(matches[0], 1)
.pipe(catchError(() => of([])))
.subscribe((assets) => {
if (assets[0]?.asset_id === matches[0]) {
this.navigate('/assets/asset/', matches[0]);
} else {
this.electrsApiService.getAsset$(matches[0])
.subscribe(
(block) => { this.navigate('/block/', matches[0], { state: { data: { block } } }); },
() => { this.navigate('/tx/', matches[0]); });
() => { this.navigate('/assets/asset/', matches[0]); },
() => {
this.electrsApiService.getBlock$(matches[0])
.subscribe(
(block) => { this.navigate('/block/', matches[0], { state: { data: { block } } }); },
() => { this.navigate('/tx/', matches[0]); });
}
);
}
);
});
} else {
this.navigate('/tx/', matches[0]);
}
@ -347,4 +351,11 @@ export class SearchFormComponent implements OnInit {
catchError(() => of([]))
);
}
getLiquidAssetSearch$(searchText: string): Observable<any[]> {
if (this.network !== 'liquid' && this.network !== 'liquidtestnet') {
return of([]);
}
return this.assetsService.searchLiquidAssets$(searchText, 10).pipe(catchError(() => of([])));
}
}

View file

@ -1,4 +1,4 @@
<div class="dropdown-menu show" *ngIf="results" [hidden]="!results.hashQuickMatch && !results.otherNetworks.length && !results.addresses.length && !results.nodes.length && !results.channels.length && !results.liquidAsset.length && !results.pools.length">
<div class="dropdown-menu show" *ngIf="results" [hidden]="!results.hashQuickMatch && !results.otherNetworks.length && !results.addresses.length && !results.nodes.length && !results.channels.length && !results.liquidAssets.length && !results.pools.length">
<ng-template [ngIf]="results.blockHeight">
<div class="card-title" i18n="search.bitcoin-block-height">{{ networkName }} Block Height</div>
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
@ -17,7 +17,7 @@
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText }"></ng-container>
</button>
</ng-template>
<ng-template [ngIf]="results.txId && !results.liquidAsset.length">
<ng-template [ngIf]="results.txId && !results.liquidAssets.length">
<div class="card-title" i18n="search.bitcoin-transaction">{{ networkName }} Transaction</div>
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : 13 }"></ng-container>
@ -81,11 +81,13 @@
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : isMobile ? 17 : 30 }"></ng-container>
</button>
</ng-template>
<ng-template [ngIf]="results.liquidAsset.length">
<ng-template [ngIf]="results.liquidAssets.length">
<div class="card-title" i18n="search.liquid-asset">Liquid Asset</div>
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : 11 }"></ng-container>&nbsp;<b>({{ results.liquidAsset[1] }})</b>
</button>
<ng-template ngFor [ngForOf]="results.liquidAssets" let-asset let-i="index">
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + results.pools.length + results.nodes.length + results.channels.length + results.otherNetworks.length + i)" [class.active]="results.hashQuickMatch + results.addresses.length + results.pools.length + results.nodes.length + results.channels.length + results.otherNetworks.length + i === activeIdx" type="button" role="option" class="dropdown-item">
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: asset.name }"></ng-container>&nbsp;<b>({{ asset.ticker }})</b>
</button>
</ng-template>
</ng-template>
</div>

View file

@ -28,7 +28,7 @@ export class SearchResultsComponent implements OnChanges {
ngOnChanges() {
this.activeIdx = 0;
if (this.results) {
this.resultsFlattened = [...(this.results.hashQuickMatch ? [this.results.searchText] : []), ...this.results.addresses, ...this.results.pools, ...this.results.nodes, ...this.results.channels, ...this.results.otherNetworks];
this.resultsFlattened = [...(this.results.hashQuickMatch ? [this.results.searchText] : []), ...this.results.addresses, ...this.results.pools, ...this.results.nodes, ...this.results.channels, ...this.results.otherNetworks, ...this.results.liquidAssets];
// If searchText is a public key corresponding to a node, select it by default
if (this.results.publicKey && this.results.nodes.length > 0) {
this.activeIdx = 1;

View file

@ -205,10 +205,25 @@ export class AssetsService {
entity: assetData[0] ? { domain: assetData[0] } : undefined,
ticker: assetData[1] || '',
name: assetData[2] || '',
})).filter((asset) => asset.name.toLowerCase().indexOf(lowerSearchText) > -1
})).filter((asset) => asset.asset_id.indexOf(lowerSearchText) > -1
|| asset.name.toLowerCase().indexOf(lowerSearchText) > -1
|| (asset.ticker || '').toLowerCase().indexOf(lowerSearchText) > -1
|| (asset.entity && asset.entity.domain || '').toLowerCase().indexOf(lowerSearchText) > -1)),
)),
switchMap((assets) => {
if (!/^[0-9a-f]{64}$/i.test(searchText) || assets.some((asset) => asset.asset_id.toLowerCase() === lowerSearchText)) {
return of(assets);
}
return this.electrsApiService.getAsset$(searchText).pipe(
map((asset) => asset.name || asset.ticker || asset.precision != null ? [{
asset_id: asset.asset_id,
entity: asset.entity,
ticker: asset.ticker || '',
name: asset.name || asset.asset_id,
}, ...assets] : assets),
catchError(() => of(assets)),
);
}),
map((assets) => assets.map((asset) => ({
...asset,
entity: asset.entity || (asset.domain ? { domain: asset.domain } : undefined),