From 8e419443a4534c1a13e08c2295db60ec9421f618 Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:24:49 +0000 Subject: [PATCH 1/4] BNS domains that resolve to addresses now searchable --- angular.json | 5 ++++- .../search-bar/search-bar.component.ts | 4 ++-- src/app/pages/account/account.component.ts | 21 +++++++++++++++---- src/app/services/api/api.service.ts | 11 ++++++++++ src/app/services/search/search.service.ts | 10 +++++++-- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/angular.json b/angular.json index 6f5cf1c..c2d22a3 100644 --- a/angular.json +++ b/angular.json @@ -151,5 +151,8 @@ } } }, - "defaultProject": "yellow-spyglass-client" + "defaultProject": "yellow-spyglass-client", + "cli": { + "analytics": false + } } diff --git a/src/app/navigation/search-bar/search-bar.component.ts b/src/app/navigation/search-bar/search-bar.component.ts index 2c93ce5..4f09450 100644 --- a/src/app/navigation/search-bar/search-bar.component.ts +++ b/src/app/navigation/search-bar/search-bar.component.ts @@ -60,7 +60,7 @@ export let APP_SEARCH_BAR_ID = 0; export class SearchBarComponent implements OnInit, AfterViewInit, AfterViewChecked { @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; - @Input() placeholder: string = 'Search by Address, Block or Alias'; + @Input() placeholder: string = 'Search by Address, Block, BNS, or Alias'; @Input() toolbarTitle: string; /** This input is used to turn off the autofocus logic. Home page search does not need autofocus, but app-bar search does. */ @@ -127,7 +127,7 @@ export class SearchBarComponent implements OnInit, AfterViewInit, AfterViewCheck return this.invalidSearch.emit(); } - if (this._searchService.isValidAddress(value) || this._searchService.isValidBlock(value)) { + if (this._searchService.isValidAddress(value) || this._searchService.isValidBlock(value) || this._searchService.isValidBNSDomain(value)) { return this._emitSearch(value, controlKey); } diff --git a/src/app/pages/account/account.component.ts b/src/app/pages/account/account.component.ts index 47a6f7f..3d447c1 100644 --- a/src/app/pages/account/account.component.ts +++ b/src/app/pages/account/account.component.ts @@ -9,7 +9,7 @@ import { OnlineRepsService } from '@app/services/online-reps/online-reps.service import { NavigationEnd, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { AliasService } from '@app/services/alias/alias.service'; -import { APP_NAV_ITEMS, hashNavItem } from '../../navigation/nav-items'; +import { APP_NAV_ITEMS, accountNavItem, hashNavItem } from '../../navigation/nav-items'; import { environment } from '../../../environments/environment'; import { DelegatorsTabService } from '@app/pages/account/tabs/delegators/delegators-tab.service'; import { InsightsTabService } from '@app/pages/account/tabs/insights/insights-tab.service'; @@ -64,12 +64,12 @@ export class AccountComponent implements OnDestroy { private readonly _insightsTabService: InsightsTabService, private readonly _delegatorsTabService: DelegatorsTabService ) { - this.routeListener = this._router.events.subscribe((route) => { + this.routeListener = this._router.events.subscribe(async (route) => { if (route instanceof NavigationEnd) { const splitUrl = this._router.url.replace('/history', '').split('/'); const path = splitUrl[splitUrl.length - 1]; const address = path.substring(0, 64); - this._searchAccount(address); + await this._searchAccount(address); } }); @@ -111,13 +111,26 @@ export class AccountComponent implements OnDestroy { void this._router.navigate([`/${hashNavItem.route}/${hash}`], { replaceUrl: true }); } + /** Call this method whenever someone has enters a BNS domain, and it is resolved to a Banano address. */ + private _redirectToAccountPage(account: string): void { + void this._router.navigate([`/${accountNavItem.route}/${account}`], { replaceUrl: true }); + } + /** Given a ban address, searches for account. */ - private _searchAccount(address): void { + private async _searchAccount(address): Promise { if (!address) { return; } if (!address.startsWith('ban_')) { + const parts = address.split("."); + if (parts.length === 2) { + //search in api + const domain = await this.apiService.fetchBNSDomain(parts[0], parts[1]); + if (domain.domain?.resolved_address) { + return this._redirectToAccountPage(domain.domain?.resolved_address); + } + } this._redirectToHashPage(address); } diff --git a/src/app/services/api/api.service.ts b/src/app/services/api/api.service.ts index ef96eab..ada9718 100644 --- a/src/app/services/api/api.service.ts +++ b/src/app/services/api/api.service.ts @@ -204,6 +204,17 @@ export class ApiService { .toPromise(); } + /** Fetch/query BNS domain. */ + async fetchBNSDomain(domain_name: string, tld: string): Promise { + await this._hasPingedApi(); + return this._http + .post(`${this.httpApi}/v1/account/bns`, { + domain_name, + tld, + }) + .toPromise(); + } + /** Fetches monitored representatives stats. */ async fetchMonitoredRepresentatives(): Promise { await this._hasPingedApi(); diff --git a/src/app/services/search/search.service.ts b/src/app/services/search/search.service.ts index e81980a..b89f500 100644 --- a/src/app/services/search/search.service.ts +++ b/src/app/services/search/search.service.ts @@ -12,14 +12,14 @@ export class SearchService { constructor(router: Router) { this.searchEvents().subscribe((data: { search: string; openInNewWindow: boolean }) => { if (data.openInNewWindow) { - if (data.search.startsWith('ban_')) { + if (data.search.startsWith('ban_') || this.isValidBNSDomain(data.search)) { const origin = window.location.origin; window.open(`${origin}/${APP_NAV_ITEMS.account.route}/${data.search}`, '_blank'); } else { window.open(`${origin}/${APP_NAV_ITEMS.hash.route}/${data.search}`, '_blank'); } } else { - if (data.search.startsWith('ban_')) { + if (data.search.startsWith('ban_') || this.isValidBNSDomain(data.search)) { void router.navigate([`${APP_NAV_ITEMS.account.route}/${data.search}`]); } else { void router.navigate([`${APP_NAV_ITEMS.hash.route}/${data.search}`]); @@ -47,4 +47,10 @@ export class SearchService { isValidBlock(block: string): boolean { return block && block.length === 64; } + + isValidBNSDomain(bns: string): boolean { + const parts = bns.split("."); + //later, can also check for illegal characters once that is more settled + return parts.length === 2 && parts[0].length <= 32; + } } From 18e7e2d363d455d447c28fda178330912929aaf9 Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:25:15 +0000 Subject: [PATCH 2/4] handle non-existent domains and run prettier --- .../navigation/search-bar/search-bar.component.ts | 6 +++++- src/app/pages/account/account.component.ts | 12 +++++++----- src/app/services/search/search.service.ts | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/app/navigation/search-bar/search-bar.component.ts b/src/app/navigation/search-bar/search-bar.component.ts index 4f09450..65eab3f 100644 --- a/src/app/navigation/search-bar/search-bar.component.ts +++ b/src/app/navigation/search-bar/search-bar.component.ts @@ -127,7 +127,11 @@ export class SearchBarComponent implements OnInit, AfterViewInit, AfterViewCheck return this.invalidSearch.emit(); } - if (this._searchService.isValidAddress(value) || this._searchService.isValidBlock(value) || this._searchService.isValidBNSDomain(value)) { + if ( + this._searchService.isValidAddress(value) || + this._searchService.isValidBlock(value) || + this._searchService.isValidBNSDomain(value) + ) { return this._emitSearch(value, controlKey); } diff --git a/src/app/pages/account/account.component.ts b/src/app/pages/account/account.component.ts index 3d447c1..8b3407f 100644 --- a/src/app/pages/account/account.component.ts +++ b/src/app/pages/account/account.component.ts @@ -123,13 +123,15 @@ export class AccountComponent implements OnDestroy { } if (!address.startsWith('ban_')) { - const parts = address.split("."); + const parts = address.split('.'); if (parts.length === 2) { //search in api - const domain = await this.apiService.fetchBNSDomain(parts[0], parts[1]); - if (domain.domain?.resolved_address) { - return this._redirectToAccountPage(domain.domain?.resolved_address); - } + try { + const domain = await this.apiService.fetchBNSDomain(parts[0], parts[1]); + if (domain.domain?.resolved_address) { + return this._redirectToAccountPage(domain.domain?.resolved_address); + } + } catch (_) {} } this._redirectToHashPage(address); } diff --git a/src/app/services/search/search.service.ts b/src/app/services/search/search.service.ts index b89f500..7f408ce 100644 --- a/src/app/services/search/search.service.ts +++ b/src/app/services/search/search.service.ts @@ -49,7 +49,7 @@ export class SearchService { } isValidBNSDomain(bns: string): boolean { - const parts = bns.split("."); + const parts = bns.split('.'); //later, can also check for illegal characters once that is more settled return parts.length === 2 && parts[0].length <= 32; } From 7f2f4133bc681f69fc6114bc302495226716635d Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:37:33 +0000 Subject: [PATCH 3/4] address review --- package.json | 1 + src/app/pages/account/account.component.ts | 6 ++-- src/app/services/api/api.service.ts | 3 +- src/app/services/search/search.service.ts | 7 ++-- src/app/services/util/util.service.ts | 6 ++++ src/app/types/dto/BNSDomainDto.ts | 5 +++ src/app/types/dto/index.ts | 2 ++ tsconfig.json | 4 +-- yarn.lock | 40 ++++++++++++++++++++++ 9 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 src/app/types/dto/BNSDomainDto.ts diff --git a/package.json b/package.json index fb5edb6..15f153e 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@brightlayer-ui/colors-branding": "^3.2.1", "@dev-ptera/nano-node-rpc": "^2.0.2", "@ngneat/until-destroy": "^9.2.3", + "banani-bns": "^0.0.9", "banano-unit-converter": "^0.1.0", "core-js": "^2.5.4", "file-saver": "^2.0.5", diff --git a/src/app/pages/account/account.component.ts b/src/app/pages/account/account.component.ts index 8b3407f..a5f6291 100644 --- a/src/app/pages/account/account.component.ts +++ b/src/app/pages/account/account.component.ts @@ -123,16 +123,18 @@ export class AccountComponent implements OnDestroy { } if (!address.startsWith('ban_')) { - const parts = address.split('.'); - if (parts.length === 2) { + //if not a banano address, and is in the format ., search in api + if (this._util.isValidBNSDomain(address)) { //search in api try { + const parts = address.split('.'); const domain = await this.apiService.fetchBNSDomain(parts[0], parts[1]); if (domain.domain?.resolved_address) { return this._redirectToAccountPage(domain.domain?.resolved_address); } } catch (_) {} } + //if not in that format, or not found in api, assume it is a block hash this._redirectToHashPage(address); } diff --git a/src/app/services/api/api.service.ts b/src/app/services/api/api.service.ts index ada9718..6db450e 100644 --- a/src/app/services/api/api.service.ts +++ b/src/app/services/api/api.service.ts @@ -11,6 +11,7 @@ import { AliasDto, BlockAtHeightDto, BlockDtoV2, + BNSDomainDto, ConfirmedTransactionDto, DelegatorsOverviewDto, DiscordResponseDto, @@ -205,7 +206,7 @@ export class ApiService { } /** Fetch/query BNS domain. */ - async fetchBNSDomain(domain_name: string, tld: string): Promise { + async fetchBNSDomain(domain_name: string, tld: string): Promise { await this._hasPingedApi(); return this._http .post(`${this.httpApi}/v1/account/bns`, { diff --git a/src/app/services/search/search.service.ts b/src/app/services/search/search.service.ts index 7f408ce..5db8cf6 100644 --- a/src/app/services/search/search.service.ts +++ b/src/app/services/search/search.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { UtilService } from '@app/services/util/util.service'; import { Observable, Subject } from 'rxjs'; import { APP_NAV_ITEMS } from '../../navigation/nav-items'; import { Router } from '@angular/router'; @@ -9,7 +10,7 @@ import { Router } from '@angular/router'; export class SearchService { search$ = new Subject<{ search: string; openInNewWindow: boolean }>(); - constructor(router: Router) { + constructor(router: Router, private readonly _utilService: UtilService) { this.searchEvents().subscribe((data: { search: string; openInNewWindow: boolean }) => { if (data.openInNewWindow) { if (data.search.startsWith('ban_') || this.isValidBNSDomain(data.search)) { @@ -49,8 +50,6 @@ export class SearchService { } isValidBNSDomain(bns: string): boolean { - const parts = bns.split('.'); - //later, can also check for illegal characters once that is more settled - return parts.length === 2 && parts[0].length <= 32; + return this._utilService.isValidBNSDomain(bns); } } diff --git a/src/app/services/util/util.service.ts b/src/app/services/util/util.service.ts index b00a404..ae78bdc 100644 --- a/src/app/services/util/util.service.ts +++ b/src/app/services/util/util.service.ts @@ -45,4 +45,10 @@ export class UtilService { shortenAddress(addr: string): string { return `${addr.substr(0, 12)}...${addr.substr(addr.length - 6, addr.length)}`; } + + isValidBNSDomain(bns: string): boolean { + const parts = bns.split('.'); + //later, can also check for illegal characters once that is more settled + return parts.length === 2 && parts[0].length <= 32; + } } diff --git a/src/app/types/dto/BNSDomainDto.ts b/src/app/types/dto/BNSDomainDto.ts new file mode 100644 index 0000000..cf790d3 --- /dev/null +++ b/src/app/types/dto/BNSDomainDto.ts @@ -0,0 +1,5 @@ +import { Domain } from 'banani-bns'; + +export type BNSDomainDto = { + domain: Domain; +}; diff --git a/src/app/types/dto/index.ts b/src/app/types/dto/index.ts index 5f52bb2..f817a3e 100644 --- a/src/app/types/dto/index.ts +++ b/src/app/types/dto/index.ts @@ -6,6 +6,7 @@ export * from './AccountOverviewDto'; export * from './AliasDto'; export * from './BlockDto'; export * from './BlockAtHeightDto'; +export * from './BNSDomainDto'; export * from './ConfirmedTransactionDto'; export * from './DelegatorDto'; export * from './DiscordResponseDto'; @@ -14,6 +15,7 @@ export * from './HostNodeStatsDto'; export * from './InsightsDto'; export * from './KnownAccountDto'; export * from './NakamotoCoefficientDto'; +export * from './BNSDomainDto'; export * from './QuorumCoefficientDto'; export * from './MonitoredRepDto'; export * from './PeerVersionsDto'; diff --git a/tsconfig.json b/tsconfig.json index e229596..a1f5c86 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,10 +17,10 @@ "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, - "target": "es2015", + "target": "es2020", "module": "es2020", "lib": [ - "es2018", + "es2020", "dom" ] }, diff --git a/yarn.lock b/yarn.lock index f22a827..7357d7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2554,6 +2554,11 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +b4a@^1.0.1: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + babel-loader@8.2.5: version "8.2.5" resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz" @@ -2611,6 +2616,20 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +banani-bns@^0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/banani-bns/-/banani-bns-0.0.9.tgz#30eea4c94fbc3f1c9cf6e0282731c7a3edc3d550" + integrity sha512-G9eRXk24ykHLqSCr2LgqNHyUvuzye0VTpNMZdyD4/8HtZRU05Pv1Dbk1lbINkz0ZVf1Y2Jl6dF0ivXMLWBqIRA== + dependencies: + banani "^1.0.4" + +banani@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/banani/-/banani-1.0.4.tgz#62a9102e70cefdbdb7826b0e66dca33ea8a64943" + integrity sha512-/W5cedmDLcrQ9T/Y17Yrk82t1EtJPf3sH14JAzgoHWgqsza2QC0YQallIOc51sY/WxEdnqLSSSoNk9hFkcd3og== + dependencies: + blake2b "^2.1.4" + banano-unit-converter@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/banano-unit-converter/-/banano-unit-converter-0.1.0.tgz" @@ -2652,6 +2671,22 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +blake2b-wasm@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be" + integrity sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w== + dependencies: + b4a "^1.0.1" + nanoassert "^2.0.0" + +blake2b@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/blake2b/-/blake2b-2.1.4.tgz#817d278526ddb4cd673bfb1af16d1ad61e393ba3" + integrity sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A== + dependencies: + blake2b-wasm "^2.4.0" + nanoassert "^2.0.0" + body-parser@1.20.0, body-parser@^1.19.0: version "1.20.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz" @@ -5878,6 +5913,11 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nanoassert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09" + integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA== + nanoid@^3.3.3, nanoid@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" From 597e6bcce3e8bceb75f5222536472ea5835b0c10 Mon Sep 17 00:00:00 2001 From: dev-ptera Date: Sat, 21 Dec 2024 07:20:15 -0500 Subject: [PATCH 4/4] 2.8.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- src/app/services/api/api.service.ts | 2 +- src/environments/environment.ts | 8 ++++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46118d2..0d60b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## v2.8.0 (December 21, 2024) + +- Added ability to search BNS addresses. + ## v2.7.0 (April 28, 2024) - Added birthday banner which displays every Apirl 1st. diff --git a/package.json b/package.json index 15f153e..6c84207 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yellow-spyglass-client", - "version": "2.7.0", + "version": "2.8.0", "scripts": { "ng": "ng", "start": "ng serve -c development --open --host 0.0.0.0", diff --git a/src/app/services/api/api.service.ts b/src/app/services/api/api.service.ts index 6db450e..00f8f49 100644 --- a/src/app/services/api/api.service.ts +++ b/src/app/services/api/api.service.ts @@ -209,7 +209,7 @@ export class ApiService { async fetchBNSDomain(domain_name: string, tld: string): Promise { await this._hasPingedApi(); return this._http - .post(`${this.httpApi}/v1/account/bns`, { + .post(`${this.httpApi}/v1/account/bns`, { domain_name, tld, }) diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 7678c68..f138894 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,8 +5,8 @@ export const environment = { production: false, brpd: false, - // api1: 'http://localhost:3001/banano', - // api2: 'http://localhost:3001/banano', - api1: 'https://api.spyglass.pw/banano', - api2: 'https://api.creeper.banano.cc/banano', + api1: 'http://localhost:3001/banano', + api2: 'http://localhost:3001/banano', + // api1: 'https://api.spyglass.pw/banano', + // api2: 'https://api.creeper.banano.cc/banano', };