From d42a3f74ec895f5b5921a2669b0daf55196d6964 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Sat, 26 Aug 2023 14:18:55 +0900 Subject: [PATCH 1/5] Add description to index html and seo service --- frontend/src/app/services/seo.service.ts | 6 ++++++ frontend/src/index.bisq.html | 18 +++++++++--------- frontend/src/index.liquid.html | 17 +++++++++-------- frontend/src/index.mempool.html | 17 +++++++++-------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 4fc25be52..37387e6bf 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -52,6 +52,12 @@ export class SeoService { this.resetTitle(); } + setDescription(newDescription: string): void { + this.metaService.updateTag({ name: 'description', content: newDescription}); + this.metaService.updateTag({ name: 'twitter:description', content: newDescription}); + this.metaService.updateTag({ property: 'og:description', content: newDescription}); + } + getTitle(): string { if (this.network === 'testnet') return this.baseTitle + ' - Bitcoin Testnet'; diff --git a/frontend/src/index.bisq.html b/frontend/src/index.bisq.html index 6d3bcb130..614c06bd4 100644 --- a/frontend/src/index.bisq.html +++ b/frontend/src/index.bisq.html @@ -7,18 +7,18 @@ - + - - - - - - - - + + + + + + + + diff --git a/frontend/src/index.liquid.html b/frontend/src/index.liquid.html index ce57e5873..37e4241a0 100644 --- a/frontend/src/index.liquid.html +++ b/frontend/src/index.liquid.html @@ -7,18 +7,19 @@ - + - - - - - - - + + + + + + + + diff --git a/frontend/src/index.mempool.html b/frontend/src/index.mempool.html index ec026b63b..e0816f6b3 100644 --- a/frontend/src/index.mempool.html +++ b/frontend/src/index.mempool.html @@ -7,18 +7,19 @@ - + - - - - - - - + + + + + + + + From d1f26f049180557990b24e1046230e30d4b77d83 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:59:29 +0900 Subject: [PATCH 2/5] Add meta descriptions to docs page Also move all meta tag setting to ngDoCheck() because page titles were not updating when user switched docs tabs. --- frontend/src/app/docs/docs/docs.component.ts | 49 ++++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index 3e74ba959..2793fd70d 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -28,21 +28,6 @@ export class DocsComponent implements OnInit { ngOnInit(): void { this.websocket.want(['blocks']); - const url = this.route.snapshot.url; - if (url[0].path === "faq" ) { - this.activeTab = 0; - this.seoService.setTitle($localize`:@@docs.faq.button-title:FAQ`); - } else if( url[1].path === "rest" ) { - this.activeTab = 1; - this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); - } else if( url[1].path === "websocket" ) { - this.activeTab = 2; - this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); - } else { - this.activeTab = 3; - this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); - } - this.env = this.stateService.env; this.showWebSocketTab = ( ! ( ( this.stateService.network === "bisq" ) || ( this.stateService.network === "liquidtestnet" ) ) ); this.showFaqTab = ( this.env.BASE_MODULE === 'mempool' ) ? true : false; @@ -51,6 +36,40 @@ export class DocsComponent implements OnInit { document.querySelector( "html" ).style.scrollBehavior = "smooth"; } + ngDoCheck(): void { + + const url = this.route.snapshot.url; + + if (url[0].path === "faq" ) { + this.activeTab = 0; + this.seoService.setTitle($localize`:@@meta.title.docs.faq:FAQ`); + this.seoService.setDescription($localize`:@@meta.description.docs.faq:Get answers to common questions like: What is a mempool? Why isn't my transaction confirming? How can I run my own instance of The Mempool Open Source Project? And more.`); + } else if( url[1].path === "rest" ) { + this.activeTab = 1; + this.seoService.setTitle($localize`:@@meta.title.docs.rest:REST API`); + if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.docs.rest-liquid:Documentation for the liquid.network REST API service: get info on addresses, transactions, assets, blocks, and more.`); + } else if( this.stateService.network === 'bisq' ) { + this.seoService.setDescription($localize`:@@meta.description.docs.rest-bisq:Documentation for the bisq.markets REST API service: get info on recent trades, current offers, transactions, network state, and more.`); + } else { + this.seoService.setDescription($localize`:@@meta.description.docs.rest-bitcoin:Documentation for the mempool.space REST API service: get info on addresses, transactions, blocks, fees, mining, the Lightning network, and more.`); + } + } else if( url[1].path === "websocket" ) { + this.activeTab = 2; + this.seoService.setTitle($localize`:@@meta.title.docs.websocket:WebSocket API`); + if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.docs.websocket-liquid:Documentation for the liquid.network WebSocket API service: get real-time info on blocks, mempools, transactions, addresses, and more.`); + } else { + this.seoService.setDescription($localize`:@@meta.description.docs.websocket-bitcoin:Documentation for the mempool.space WebSocket API service: get real-time info on blocks, mempools, transactions, addresses, and more.`); + } + } else { + this.activeTab = 3; + this.seoService.setTitle($localize`:@@meta.title.docs.websocket:Electrum RPC`); + this.seoService.setDescription($localize`:@@meta.description.docs.electrumrpc:Documentation for our Electrum RPC interface: get instant, convenient, and reliable access to an Esplora instance.`); + } + + } + ngOnDestroy(): void { document.querySelector( "html" ).style.scrollBehavior = "auto"; } From 6312884234fa33e10b45aae8c26eaa5f2f7332bb Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:10:08 +0900 Subject: [PATCH 3/5] Add meta descriptions for bisq --- .../bisq/bisq-address/bisq-address.component.ts | 1 + .../app/bisq/bisq-block/bisq-block.component.ts | 1 + .../bisq/bisq-blocks/bisq-blocks.component.ts | 1 + .../bisq-dashboard/bisq-dashboard.component.ts | 3 ++- .../bisq-main-dashboard.component.ts | 1 + .../bisq/bisq-market/bisq-market.component.ts | 3 ++- .../app/bisq/bisq-stats/bisq-stats.component.ts | 1 + .../bisq-transaction.component.ts | 1 + .../bisq-transactions.component.ts | 1 + frontend/src/app/services/seo.service.ts | 16 ++++++++++++++++ 10 files changed, 27 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts index 7711c4d3c..1e4f0a178 100644 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts +++ b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts @@ -41,6 +41,7 @@ export class BisqAddressComponent implements OnInit, OnDestroy { document.body.scrollTo(0, 0); this.addressString = params.get('id') || ''; this.seoService.setTitle($localize`:@@bisq-address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); + this.seoService.setDescription($localize`:@@meta.description.bisq.address:See current balance, pending transactions, and history of confirmed transactions for BSQ address ${this.addressString}:INTERPOLATION:.`); return this.bisqApiService.getAddress$(this.addressString) .pipe( diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts index 206a18031..42f62fbc0 100644 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts +++ b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts @@ -88,6 +88,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy { this.isLoading = false; this.blockHeight = block.height; this.seoService.setTitle($localize`:@@bisq-block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.hash}:BLOCK_HASH:`); + this.seoService.setDescription($localize`:@@meta.description.bisq.block:See all BSQ transactions in Bitcoin block ${block.height}:BLOCK_HEIGHT: (block hash ${block.hash}:BLOCK_HASH:).`); this.block = block; }); } diff --git a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts index 8d9ed3c11..7ab742655 100644 --- a/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts +++ b/frontend/src/app/bisq/bisq-blocks/bisq-blocks.component.ts @@ -36,6 +36,7 @@ export class BisqBlocksComponent implements OnInit { ngOnInit(): void { this.websocketService.want(['blocks']); this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); + this.seoService.setDescription($localize`:@@meta.description.bisq.blocks:See a list of recent Bitcoin blocks with BSQ transactions, total BSQ sent per block, and more.`); this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10); this.loadingItems = Array(this.itemsPerPage); if (document.body.clientWidth < 670) { diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts index fe36f1b53..ebcb8a067 100644 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts +++ b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts @@ -29,7 +29,8 @@ export class BisqDashboardComponent implements OnInit { ) { } ngOnInit(): void { - this.seoService.setTitle(`Markets`); + this.seoService.setTitle($localize`:@@meta.title.bisq.markets:Markets`); + this.seoService.setDescription($localize`:@@meta.description.bisq.markets:Explore the full Bitcoin ecosystem with The Mempool Open Project™. See Bisq market prices, trading activity, and more.`); this.websocketService.want(['blocks']); this.volumes$ = this.bisqApiService.getAllVolumesDay$() diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts index d1b8480f7..e7e4471c9 100644 --- a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts +++ b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts @@ -34,6 +34,7 @@ export class BisqMainDashboardComponent implements OnInit { ngOnInit(): void { this.seoService.resetTitle(); + this.seoService.resetDescription(); this.websocketService.want(['blocks']); this.usdPrice$ = this.stateService.conversions$.asObservable().pipe( diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts index c9dde4115..f81b4d891 100644 --- a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts +++ b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts @@ -48,7 +48,8 @@ export class BisqMarketComponent implements OnInit, OnDestroy { map(([markets, routeParams]) => { const pair = routeParams.get('pair'); const pairUpperCase = pair.replace('_', '/').toUpperCase(); - this.seoService.setTitle(`Bisq market: ${pairUpperCase}`); + this.seoService.setTitle($localize`:@@meta.title.bisq.market:Bisq market: ${pairUpperCase}`); + this.seoService.setDescription($localize`:@@meta.description.bisq.market:See price history, current buy/sell offers, and latest trades for the ${pairUpperCase} market on Bisq.`); return { pair: pairUpperCase, diff --git a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts index 5ec5964b4..58819d9cf 100644 --- a/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts +++ b/frontend/src/app/bisq/bisq-stats/bisq-stats.component.ts @@ -26,6 +26,7 @@ export class BisqStatsComponent implements OnInit { this.websocketService.want(['blocks']); this.seoService.setTitle($localize`:@@2a30a4cdb123a03facc5ab8c5b3e6d8b8dbbc3d4:BSQ statistics`); + this.seoService.setDescription($localize`:@@meta.description.bisq.stats:See high-level stats on the BSQ economy: supply metrics, number of addresses, BSQ price, market cap, and more.`); this.stateService.bsqPrice$ .subscribe((bsqPrice) => { this.price = bsqPrice; diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts index 4951643c8..1818e105f 100644 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts +++ b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts @@ -48,6 +48,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy { document.body.scrollTo(0, 0); this.txId = params.get('id') || ''; this.seoService.setTitle($localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`); + this.seoService.setDescription($localize`:@@meta.description.bisq.transaction:See inputs, outputs, transaction type, burnt amount, and more for transaction with txid ${this.txId}:INTERPOLATION:.`); if (history.state.data) { return of(history.state.data); } diff --git a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts index 212030e77..be5455639 100644 --- a/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts +++ b/frontend/src/app/bisq/bisq-transactions/bisq-transactions.component.ts @@ -79,6 +79,7 @@ export class BisqTransactionsComponent implements OnInit, OnDestroy { ngOnInit(): void { this.websocketService.want(['blocks']); this.seoService.setTitle($localize`:@@add4cd82e3e38a3110fe67b3c7df56e9602644ee:Transactions`); + this.seoService.setDescription($localize`:@@meta.description.bisq.transactions:See recent BSQ transactions: amount, txid, associated Bitcoin block, transaction type, and more.`); this.radioGroupForm = this.formBuilder.group({ txTypes: [this.txTypesDefaultChecked], diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 37387e6bf..2584cbaed 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -10,6 +10,7 @@ import { StateService } from './state.service'; export class SeoService { network = ''; baseTitle = 'mempool'; + baseDescription = 'Explore the full Bitcoin ecosystem with The Mempool Open Project™.'; constructor( private titleService: Title, @@ -58,6 +59,12 @@ export class SeoService { this.metaService.updateTag({ property: 'og:description', content: newDescription}); } + resetDescription(): void { + this.metaService.updateTag({ name: 'description', content: this.getDescription()}); + this.metaService.updateTag({ name: 'twitter:description', content: this.getDescription()}); + this.metaService.updateTag({ property: 'og:description', content: this.getDescription()}); + } + getTitle(): string { if (this.network === 'testnet') return this.baseTitle + ' - Bitcoin Testnet'; @@ -72,6 +79,15 @@ export class SeoService { return this.baseTitle + ' - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer'; } + getDescription(): string { + if ( (this.network === 'testnet') || (this.network === 'signet') || (this.network === '') || (this.network == 'mainnet') ) + return this.baseDescription + ' See the real-time status of your transactions, browse network stats, and more.'; + if ( (this.network === 'liquid') || (this.network === 'liquidtestnet') ) + return this.baseDescription + ' See Liquid transactions & assets, get network info, and more.'; + if (this.network === 'bisq') + return this.baseDescription + ' See Bisq market prices, trading activity, and more.'; + } + ucfirst(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } From cd366177badd52be195245ae46cd71c35ac97118 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 30 Aug 2023 20:26:07 +0900 Subject: [PATCH 4/5] Add meta descriptions for mempool and liquid --- .../app/components/about/about.component.ts | 1 + .../address/address-preview.component.ts | 2 ++ .../components/address/address.component.ts | 2 ++ .../assets/assets-nav/assets-nav.component.ts | 1 + .../block-fee-rates-graph.component.ts | 1 + .../block-fees-graph.component.ts | 3 ++- .../block-health-graph.component.ts | 1 + .../block-rewards-graph.component.ts | 3 ++- .../block-sizes-weights-graph.component.ts | 1 + .../block/block-preview.component.ts | 6 ++++++ .../app/components/block/block.component.ts | 12 +++++++++--- .../hashrate-chart.component.ts | 4 +++- .../mempool-block/mempool-block.component.ts | 2 ++ .../mining-dashboard.component.ts | 3 ++- .../pool-ranking/pool-ranking.component.ts | 3 ++- .../components/pool/pool-preview.component.ts | 1 + .../src/app/components/pool/pool.component.ts | 1 + .../statistics/statistics.component.ts | 1 + .../television/television.component.ts | 1 + .../transaction-preview.component.ts | 2 ++ .../transaction/transaction.component.ts | 4 +++- .../src/app/dashboard/dashboard.component.ts | 1 + .../channel/channel-preview.component.ts | 1 + .../lightning/channel/channel.component.ts | 1 + .../group/group-preview.component.ts | 1 + .../app/lightning/group/group.component.ts | 1 + .../lightning-dashboard.component.ts | 1 + .../lightning/node/node-preview.component.ts | 1 + .../src/app/lightning/node/node.component.ts | 1 + .../nodes-channels-map.component.ts | 19 ++++++++++--------- .../nodes-map/nodes-map.component.ts | 1 + .../nodes-networks-chart.component.ts | 3 ++- .../nodes-per-country-chart.component.ts | 2 +- .../nodes-per-country.component.ts | 7 ++++--- .../nodes-per-isp-preview.component.ts | 1 + .../nodes-per-isp/nodes-per-isp.component.ts | 1 + .../oldest-nodes/oldest-nodes.component.ts | 1 + .../top-nodes-per-capacity.component.ts | 3 ++- .../top-nodes-per-channels.component.ts | 1 + .../nodes-rankings-dashboard.component.ts | 1 + .../lightning-statistics-chart.component.ts | 1 + frontend/src/app/shared/common.utils.ts | 11 ++++++++++- 42 files changed, 90 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/components/about/about.component.ts b/frontend/src/app/components/about/about.component.ts index 4bf7869de..3cda0dc20 100644 --- a/frontend/src/app/components/about/about.component.ts +++ b/frontend/src/app/components/about/about.component.ts @@ -43,6 +43,7 @@ export class AboutComponent implements OnInit { ngOnInit() { this.backendInfo$ = this.stateService.backendInfo$; this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`); + this.seoService.setDescription($localize`:@@meta.description.about:Learn more about The Mempool Open Source Project™\: enterprise sponsors, individual sponsors, integrations, who contributes, FOSS licensing, and more.`); this.websocketService.want(['blocks']); this.profiles$ = this.apiService.getAboutPageProfiles$().pipe( diff --git a/frontend/src/app/components/address/address-preview.component.ts b/frontend/src/app/components/address/address-preview.component.ts index 844def9fd..9bc6e967f 100644 --- a/frontend/src/app/components/address/address-preview.component.ts +++ b/frontend/src/app/components/address/address-preview.component.ts @@ -9,6 +9,7 @@ import { AudioService } from '../../services/audio.service'; import { ApiService } from '../../services/api.service'; import { of, merge, Subscription, Observable } from 'rxjs'; import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { AddressInformation } from '../../interfaces/node-api.interface'; @Component({ @@ -68,6 +69,7 @@ export class AddressPreviewComponent implements OnInit, OnDestroy { this.addressString = this.addressString.toLowerCase(); } this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.address:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} address ${this.addressString}:INTERPOLATION:.`); return (this.addressString.match(/04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}/) ? this.electrsApiService.getPubKeyAddress$(this.addressString) diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 3c79f2823..e9cd0189b 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -9,6 +9,7 @@ import { AudioService } from '../../services/audio.service'; import { ApiService } from '../../services/api.service'; import { of, merge, Subscription, Observable } from 'rxjs'; import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { AddressInformation } from '../../interfaces/node-api.interface'; @Component({ @@ -76,6 +77,7 @@ export class AddressComponent implements OnInit, OnDestroy { this.addressString = this.addressString.toLowerCase(); } this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.address:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} address ${this.addressString}:INTERPOLATION:.`); return merge( of(true), diff --git a/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts index bc38d3c10..c9b044b34 100644 --- a/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts +++ b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts @@ -40,6 +40,7 @@ export class AssetsNavComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@ee8f8008bae6ce3a49840c4e1d39b4af23d4c263:Assets`); + this.seoService.setDescription($localize`:@@meta.description.liquid.assets:Explore all the assets issued on the Liquid network like L-BTC, L-CAD, USDT, and more.`); this.typeaheadSearchFn = this.typeaheadSearch; this.searchForm = this.formBuilder.group({ diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 66a594643..b4c4e9a3b 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -64,6 +64,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@ed8e33059967f554ff06b4f5b6049c465b92d9b3:Block Fee Rates`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-fee-rates:See Bitcoin feerates visualized over time, including minimum and maximum feerates per block along with feerates at various percentiles.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts index 051d24848..722929f9e 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts @@ -65,6 +65,7 @@ export class BlockFeesGraphComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Block Fees`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-fees:See the average mining fees earned per Bitcoin block visualized in BTC and USD over time.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('1m'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); @@ -192,7 +193,7 @@ export class BlockFeesGraphComponent implements OnInit { { name: 'Fees ' + this.currency, inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts index 46aebdd6e..299044dbb 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts @@ -61,6 +61,7 @@ export class BlockHealthGraphComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@d7d5fcf50179ad70c938491c517efb82de2c8146:Block Health`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-health:See Bitcoin block health visualized over time. Block health is a measure of how many expected transactions were included in an actual mined block. Expected transactions are determined using Mempool's re-implementation of Bitcoin Core's transaction selection algorithm.`); this.miningWindowPreference = '24h';//this.miningService.getDefaultTimespan('24h'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts index 2d8a6f858..505da17a5 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts @@ -63,6 +63,7 @@ export class BlockRewardsGraphComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@8ba8fe810458280a83df7fdf4c614dfc1a826445:Block Rewards`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-rewards:See Bitcoin block rewards in BTC and USD visualized over time. Block rewards are the total funds miners earn from the block subsidy and fees.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('3m'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); @@ -191,7 +192,7 @@ export class BlockRewardsGraphComponent implements OnInit { { name: 'Rewards ' + this.currency, inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts index 8477af588..e42c6a8df 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts @@ -60,6 +60,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit { let firstRun = true; this.seoService.setTitle($localize`:@@56fa1cd221491b6478998679cba2dc8d55ba330d:Block Sizes and Weights`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-sizes:See Bitcoin block sizes (MB) and block weights (weight units) visualized over time.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); diff --git a/frontend/src/app/components/block/block-preview.component.ts b/frontend/src/app/components/block/block-preview.component.ts index 7c10dab6f..c4dfe40df 100644 --- a/frontend/src/app/components/block/block-preview.component.ts +++ b/frontend/src/app/components/block/block-preview.component.ts @@ -8,6 +8,7 @@ import { SeoService } from '../../services/seo.service'; import { OpenGraphService } from '../../services/opengraph.service'; import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; @Component({ @@ -97,6 +98,11 @@ export class BlockPreviewComponent implements OnInit, OnDestroy { this.blockHeight = block.height; this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`); + if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.liquid.block:See size, weight, fee range, included transactions, and more for Liquid${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } else { + this.seoService.setDescription($localize`:@@meta.description.bitcoin.block:See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } this.isLoadingBlock = false; this.setBlockSubsidy(); if (block?.extras?.reward !== undefined) { diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 5d5233512..e226807e3 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -13,6 +13,7 @@ import { BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces import { ApiService } from '../../services/api.service'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; import { detectWebGL } from '../../shared/graphs.utils'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { PriceService, Price } from '../../services/price.service'; import { CacheService } from '../../services/cache.service'; @@ -261,6 +262,11 @@ export class BlockComponent implements OnInit, OnDestroy { this.setNextAndPreviousBlockLink(); this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`); + if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.liquid.block:See size, weight, fee range, included transactions, and more for Liquid${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } else { + this.seoService.setDescription($localize`:@@meta.description.bitcoin.block:See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } this.isLoadingBlock = false; this.setBlockSubsidy(); if (block?.extras?.reward !== undefined) { @@ -325,7 +331,7 @@ export class BlockComponent implements OnInit, OnDestroy { ]); }) ) - .subscribe(([transactions, blockAudit]) => { + .subscribe(([transactions, blockAudit]) => { if (transactions) { this.strippedTransactions = transactions; } else { @@ -680,7 +686,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.setAuditAvailable(false); } } - + isAuditAvailableFromBlockHeight(blockHeight: number): boolean { if (!this.auditSupported) { return false; @@ -729,4 +735,4 @@ export class BlockComponent implements OnInit, OnDestroy { this.block.canonical = block.id; } } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 62cc71ca6..592aba60b 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -12,6 +12,7 @@ import { MiningService } from '../../services/mining.service'; import { download } from '../../shared/graphs.utils'; import { ActivatedRoute } from '@angular/router'; import { StateService } from '../../services/state.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ selector: 'app-hashrate-chart', @@ -71,6 +72,7 @@ export class HashrateChartComponent implements OnInit { this.miningWindowPreference = '1y'; } else { this.seoService.setTitle($localize`:@@3510fc6daa1d975f331e3a717bdf1a34efa06dff:Hashrate & Difficulty`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.hashrate:See hashrate and difficulty for the Bitcoin${seoDescriptionNetwork(this.network)} network visualized over time.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('3m'); } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); @@ -256,7 +258,7 @@ export class HashrateChartComponent implements OnInit { let difficultyPowerOfTen = hashratePowerOfTen; let difficulty = tick.data[1]; if (difficulty === null) { - difficultyString = `${tick.marker} ${tick.seriesName}: No data
`; + difficultyString = `${tick.marker} ${tick.seriesName}: No data
`; } else { if (this.isMobile()) { difficultyPowerOfTen = selectPowerOfTen(tick.data[1]); diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index 6e0b21196..c11bedacd 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -5,6 +5,7 @@ import { switchMap, map, tap, filter } from 'rxjs/operators'; import { MempoolBlock, TransactionStripped } from '../../interfaces/websocket.interface'; import { Observable, BehaviorSubject } from 'rxjs'; import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { WebsocketService } from '../../services/websocket.service'; @Component({ @@ -54,6 +55,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy { const ordinal = this.getOrdinal(mempoolBlocks[this.mempoolBlockIndex]); this.ordinal$.next(ordinal); this.seoService.setTitle(ordinal); + this.seoService.setDescription($localize`:@@meta.description.mempool-block:See stats for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} transactions in the mempool: fee range, aggregate size, and more. Mempool blocks are updated in real-time as the network receives new transactions.`); mempoolBlocks[this.mempoolBlockIndex].isStack = mempoolBlocks[this.mempoolBlockIndex].blockVSize > this.stateService.blockVSize; return mempoolBlocks[this.mempoolBlockIndex]; }) diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index 6353ab8b8..b3b2093ce 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -18,6 +18,7 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { private router: Router ) { this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`); + this.seoService.setDescription($localize`:@@meta.description.mining.dashboard:Get real-time Bitcoin mining stats like hashrate, difficulty adjustment, block rewards, pool dominance, and more.`); } ngOnInit(): void { @@ -29,7 +30,7 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { this.router.events.subscribe((e: NavigationStart) => { if (e.type === EventType.NavigationStart) { if (e.url.indexOf('graphs') === -1) { // The mining dashboard and the graph component are part of the same module so we can't use ngAfterViewInit in graphs.component.ts to blur the input - this.stateService.focusSearchInputDesktop(); + this.stateService.focusSearchInputDesktop(); } } }); diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index ea3a52e8e..91475040c 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -56,6 +56,7 @@ export class PoolRankingComponent implements OnInit { this.miningWindowPreference = '1w'; } else { this.seoService.setTitle($localize`:@@mining.mining-pools:Mining Pools`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.pool-ranking:See the top Bitcoin mining pools ranked by number of blocks mined, over your desired timeframe.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); @@ -116,7 +117,7 @@ export class PoolRankingComponent implements OnInit { } else if (this.widget) { poolShareThreshold = 1; } - + const data: object[] = []; let totalShareOther = 0; let totalBlockOther = 0; diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts index 927291f07..b2302b9a7 100644 --- a/frontend/src/app/components/pool/pool-preview.component.ts +++ b/frontend/src/app/components/pool/pool-preview.component.ts @@ -83,6 +83,7 @@ export class PoolPreviewComponent implements OnInit { } this.seoService.setTitle(poolStats.pool.name); + this.seoService.setDescription($localize`:@@meta.description.mining.pool:See mining pool stats for ${poolStats.pool.name}\: most recent mined blocks, hashrate over time, total block reward to date, known coinbase addresses, and more.`); let regexes = '"'; for (const regex of poolStats.pool.regexes) { regexes += regex + '", "'; diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index a14d2a9b0..0d465bc3c 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -83,6 +83,7 @@ export class PoolComponent implements OnInit { }), map((poolStats) => { this.seoService.setTitle(poolStats.pool.name); + this.seoService.setDescription($localize`:@@meta.description.mining.pool:See mining pool stats for ${poolStats.pool.name}\: most recent mined blocks, hashrate over time, total block reward to date, known coinbase addresses, and more.`); let regexes = '"'; for (const regex of poolStats.pool.regexes) { regexes += regex + '", "'; diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts index eebee5bce..69bf9ea8f 100644 --- a/frontend/src/app/components/statistics/statistics.component.ts +++ b/frontend/src/app/components/statistics/statistics.component.ts @@ -62,6 +62,7 @@ export class StatisticsComponent implements OnInit { this.inverted = this.storageService.getValue('inverted-graph') === 'true'; this.setFeeLevelDropdownData(); this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.mempool:See mempool size (in MvB) and transactions per second (in vB/s) visualized over time.`); this.stateService.networkChanged$.subscribe((network) => this.network = network); this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h'; diff --git a/frontend/src/app/components/television/television.component.ts b/frontend/src/app/components/television/television.component.ts index 5e3888aa4..3ac8ef648 100644 --- a/frontend/src/app/components/television/television.component.ts +++ b/frontend/src/app/components/television/television.component.ts @@ -37,6 +37,7 @@ export class TelevisionComponent implements OnInit, OnDestroy { ngOnInit() { this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`); + this.seoService.setDescription($localize`:@@meta.description.tv:See Bitcoin blocks and mempool congestion in real-time in a simplified format perfect for a TV.`); this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']); this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { diff --git a/frontend/src/app/components/transaction/transaction-preview.component.ts b/frontend/src/app/components/transaction/transaction-preview.component.ts index 07843cc57..153d9b22a 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.ts +++ b/frontend/src/app/components/transaction/transaction-preview.component.ts @@ -15,6 +15,7 @@ import { CacheService } from '../../services/cache.service'; import { OpenGraphService } from '../../services/opengraph.service'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { CpfpInfo } from '../../interfaces/node-api.interface'; import { LiquidUnblinding } from './liquid-ublinding'; @@ -87,6 +88,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { this.seoService.setTitle( $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` ); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.transaction:Get real-time status, addresses, fees, script info, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} transaction with txid {txid}.`); this.resetTransaction(); return merge( of(true), diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index f1f3850e4..abc8b93b8 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -19,6 +19,7 @@ import { WebsocketService } from '../../services/websocket.service'; import { AudioService } from '../../services/audio.service'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment } from '../../interfaces/node-api.interface'; import { LiquidUnblinding } from './liquid-ublinding'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; @@ -297,6 +298,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.seoService.setTitle( $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` ); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.transaction:Get real-time status, addresses, fees, script info, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} transaction with txid {txid}.`); this.resetTransaction(); return merge( of(true), @@ -399,7 +401,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.blockConversion = price; }) ).subscribe(); - + setTimeout(() => { this.applyFragment(); }, 0); }, (error) => { diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 31fabd06f..8a34bf768 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -69,6 +69,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { ngOnInit(): void { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); + this.seoService.resetDescription(); this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']); this.websocketService.startTrackRbfSummary(); this.network$ = merge(of(''), this.stateService.networkChanged$); diff --git a/frontend/src/app/lightning/channel/channel-preview.component.ts b/frontend/src/app/lightning/channel/channel-preview.component.ts index 9c7fdc1d6..7e3152513 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.ts +++ b/frontend/src/app/lightning/channel/channel-preview.component.ts @@ -34,6 +34,7 @@ export class ChannelPreviewComponent implements OnInit { this.openGraphService.waitFor('channel-data-' + this.shortId); this.error = null; this.seoService.setTitle(`Channel: ${params.get('short_id')}`); + this.seoService.setDescription($localize`:@@meta.description.lightning.channel:Overview for Lightning channel ${params.get('short_id')}. See channel capacity, the Lightning nodes involved, related on-chain transactions, and more.`); return this.lightningApiService.getChannel$(params.get('short_id')) .pipe( tap((data) => { diff --git a/frontend/src/app/lightning/channel/channel.component.ts b/frontend/src/app/lightning/channel/channel.component.ts index 052225cc3..a26101bdb 100644 --- a/frontend/src/app/lightning/channel/channel.component.ts +++ b/frontend/src/app/lightning/channel/channel.component.ts @@ -35,6 +35,7 @@ export class ChannelComponent implements OnInit { .pipe( tap((value) => { this.seoService.setTitle($localize`Channel: ${value.short_id}`); + this.seoService.setDescription($localize`:@@meta.description.lightning.channel:Overview for Lightning channel ${value.short_id}. See channel capacity, the Lightning nodes involved, related on-chain transactions, and more.`); }), catchError((err) => { this.error = err; diff --git a/frontend/src/app/lightning/group/group-preview.component.ts b/frontend/src/app/lightning/group/group-preview.component.ts index 5fd730931..fc81eab38 100644 --- a/frontend/src/app/lightning/group/group-preview.component.ts +++ b/frontend/src/app/lightning/group/group-preview.component.ts @@ -31,6 +31,7 @@ export class GroupPreviewComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle(`Mempool.Space Lightning Nodes`); + this.seoService.setDescription(`See all Lightning nodes run by mempool.space -- these are the nodes that provide the data on the mempool.space Lightning dashboard.`); this.nodes$ = this.activatedRoute.paramMap .pipe( diff --git a/frontend/src/app/lightning/group/group.component.ts b/frontend/src/app/lightning/group/group.component.ts index 71ca17a4a..0786076ed 100644 --- a/frontend/src/app/lightning/group/group.component.ts +++ b/frontend/src/app/lightning/group/group.component.ts @@ -39,6 +39,7 @@ export class GroupComponent implements OnInit { }); this.seoService.setTitle(`Mempool.space Lightning Nodes`); + this.seoService.setDescription(`See all Lightning nodes run by mempool.space -- these are the nodes that provide the data on the mempool.space Lightning dashboard.`); this.nodes$ = this.lightningApiService.getNodGroupNodes$('mempool.space') .pipe( diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index e58d5f124..ba5ee3db2 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -25,6 +25,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`); + this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc) and Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`); this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share()); this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share()); diff --git a/frontend/src/app/lightning/node/node-preview.component.ts b/frontend/src/app/lightning/node/node-preview.component.ts index 56753b18b..d47a8c5ad 100644 --- a/frontend/src/app/lightning/node/node-preview.component.ts +++ b/frontend/src/app/lightning/node/node-preview.component.ts @@ -49,6 +49,7 @@ export class NodePreviewComponent implements OnInit { }), map((node) => { this.seoService.setTitle(`Node: ${node.alias}`); + this.seoService.setDescription($localize`:@@meta.description.lightning.node:Overview for the Lightning network node named ${node.alias}. See channels, capacity, location, fee stats, and more.`); const socketsObject = []; const socketTypesMap = {}; diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index 06ae50df2..56f48bf65 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -60,6 +60,7 @@ export class NodeComponent implements OnInit { }), map((node) => { this.seoService.setTitle($localize`Node: ${node.alias}`); + this.seoService.setDescription($localize`:@@meta.description.lightning.node:Overview for the Lightning network node named ${node.alias}. See channels, capacity, location, fee stats, and more.`); this.clearnetSocketCount = 0; this.torSocketCount = 0; diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts index bf4117b30..3090a803c 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts @@ -26,7 +26,7 @@ export class NodesChannelsMap implements OnInit { @Input() disableSpinner = false; @Output() readyEvent = new EventEmitter(); - channelsObservable: Observable; + channelsObservable: Observable; center: number[] | undefined; zoom: number | undefined; @@ -41,7 +41,7 @@ export class NodesChannelsMap implements OnInit { chartOptions: EChartsOption = {}; chartInitOptions = { renderer: 'canvas', - }; + }; constructor( private seoService: SeoService, @@ -64,15 +64,16 @@ export class NodesChannelsMap implements OnInit { this.zoom = 1.4; this.center = [0, 10]; } - + if (this.style === 'graph') { this.seoService.setTitle($localize`Lightning Nodes Channels World Map`); + this.seoService.setDescription($localize`:@@meta.description.lightning.node-map:See the channels of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details.`); } if (['nodepage', 'channelpage'].includes(this.style)) { this.nodeSize = 8; } - + this.channelsObservable = this.activatedRoute.paramMap .pipe( delay(100), @@ -81,7 +82,7 @@ export class NodesChannelsMap implements OnInit { if (this.style === 'channelpage' && this.channel.length === 0 || !this.hasLocation) { this.isLoading = false; } - + return zip( this.assetsService.getWorldMapJson$, this.style !== 'channelpage' ? this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined, this.style) : [''], @@ -140,7 +141,7 @@ export class NodesChannelsMap implements OnInit { // on top of each other let random = Math.random() * 2 * Math.PI; let random2 = Math.random() * 0.01; - + if (!nodesPubkeys[node1UniqueId]) { nodes.push([ channel[node1GpsLat] + random2 * Math.cos(random), @@ -167,7 +168,7 @@ export class NodesChannelsMap implements OnInit { } const channelLoc = []; - channelLoc.push(nodesPubkeys[node1UniqueId].slice(0, 2)); + channelLoc.push(nodesPubkeys[node1UniqueId].slice(0, 2)); channelLoc.push(nodesPubkeys[node2UniqueId].slice(0, 2)); channelsLoc.push(channelLoc); } @@ -326,7 +327,7 @@ export class NodesChannelsMap implements OnInit { this.chartInstance.on('finished', () => { this.isLoading = false; }); - + if (this.style === 'widget') { this.chartInstance.getZr().on('click', (e) => { this.zone.run(() => { @@ -335,7 +336,7 @@ export class NodesChannelsMap implements OnInit { }); }); } - + this.chartInstance.on('click', (e) => { if (e.data) { this.zone.run(() => { diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index e0fd80a53..4f74723cc 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -48,6 +48,7 @@ export class NodesMap implements OnInit, OnChanges { ngOnInit(): void { if (!this.widget) { this.seoService.setTitle($localize`:@@af8560ca50882114be16c951650f83bca73161a7:Lightning Nodes World Map`); + this.seoService.setDescription($localize`:@@meta.description.lightning.node-channel-map:See the locations of non-Tor Lightning network nodes visualized on a world map. Hover/tap on points on the map for node names and details.`); } if (!this.inputNodes$) { diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts index db04e9e00..f62a6a244 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts @@ -65,6 +65,7 @@ export class NodesNetworksChartComponent implements OnInit { this.miningWindowPreference = '3y'; } else { this.seoService.setTitle($localize`:@@b420668a91f8ebaf6e6409c4ba87f1d45961d2bd:Lightning Nodes Per Network`); + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-network:See the number of Lightning network nodes visualized over time by network: clearnet only (IPv4, IPv6), darknet (Tor, I2p, cjdns), and both.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('all'); } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); @@ -375,7 +376,7 @@ export class NodesNetworksChartComponent implements OnInit { // We create dummy duplicated series so when we use the data zoom, the y axis // both scales properly const invisibleSerie = {...serie}; - invisibleSerie.name = 'ignored' + Math.random().toString(); + invisibleSerie.name = 'ignored' + Math.random().toString(); invisibleSerie.stack = 'ignored'; invisibleSerie.yAxisIndex = 1; invisibleSerie.lineStyle = { diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts index b4621d7bf..5bfa0fc2c 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts @@ -44,7 +44,7 @@ export class NodesPerCountryChartComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@9d3ad4c6623870d96b65fb7a708fed6ce7c20044:Lightning Nodes Per Country`); - + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-country-overview:See a geographical breakdown of the Lightning network: how many Lightning nodes are hosted in countries around the world, aggregate BTC capacity for each country, and more.`); this.nodesPerCountryObservable$ = this.apiService.getNodesPerCountry$() .pipe( map(data => { diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts index 01eb6d1cf..19dd999ee 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -33,6 +33,7 @@ export class NodesPerCountry implements OnInit { .pipe( map(response => { this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`); + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-country:Explore all the Lightning nodes hosted in ${response.country.en} and see an overview of each node's capacity, number of open channels, and more.`); this.country = { name: response.country.en, @@ -47,7 +48,7 @@ export class NodesPerCountry implements OnInit { iso: response.nodes[i].iso_code, }; } - + const sumLiquidity = response.nodes.reduce((partialSum, a) => partialSum + a.capacity, 0); const sumChannels = response.nodes.reduce((partialSum, a) => partialSum + a.channels, 0); const isps = {}; @@ -70,14 +71,14 @@ export class NodesPerCountry implements OnInit { isps[node.isp].asns.push(node.as_number); } isps[node.isp].count++; - + if (isps[node.isp].count > topIsp.count) { topIsp.count = isps[node.isp].count; topIsp.id = isps[node.isp].asns.join(','); topIsp.name = node.isp; } } - + return { nodes: response.nodes, sumLiquidity: sumLiquidity, diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts index b823a5188..313353ab8 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts @@ -42,6 +42,7 @@ export class NodesPerISPPreview implements OnInit { id: this.route.snapshot.params.isp.split(',').join(', ') }; this.seoService.setTitle($localize`Lightning nodes on ISP: ${response.isp} [AS${this.route.snapshot.params.isp}]`); + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-isp:Browse all Bitcoin Lightning nodes using the ${response.isp} [AS${this.route.snapshot.params.isp}] ISP and see aggregate stats like total number of nodes, total capacity, and more for the ISP.`); for (const i in response.nodes) { response.nodes[i].geolocation = { diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts index e87482583..d4f27975c 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts @@ -37,6 +37,7 @@ export class NodesPerISP implements OnInit { id: this.route.snapshot.params.isp.split(',').join(', ') }; this.seoService.setTitle($localize`Lightning nodes on ISP: ${response.isp} [AS${this.route.snapshot.params.isp}]`); + this.seoService.setDescription($localize`:@@meta.description.lightning.nodes-isp:Browse all Bitcoin Lightning nodes using the ${response.isp} [AS${this.route.snapshot.params.isp}] ISP and see aggregate stats like total number of nodes, total capacity, and more for the ISP.`); for (const i in response.nodes) { response.nodes[i].geolocation = { diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts index 6ee9ed231..d83f3db0a 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts @@ -25,6 +25,7 @@ export class OldestNodes implements OnInit { ngOnInit(): void { if (!this.widget) { this.seoService.setTitle($localize`Oldest lightning nodes`); + this.seoService.setDescription($localize`:@@meta.description.lightning.ranking.oldest:See the oldest nodes on the Lightning network along with their capacity, number of channels, location, etc.`); } for (let i = 1; i <= (this.widget ? 10 : 100); ++i) { diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts index 50190f5ab..054fa2f3c 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts @@ -15,7 +15,7 @@ import { LightningApiService } from '../../lightning-api.service'; export class TopNodesPerCapacity implements OnInit { @Input() nodes$: Observable; @Input() widget: boolean = false; - + topNodesPerCapacity$: Observable; skeletonRows: number[] = []; currency$: Observable; @@ -31,6 +31,7 @@ export class TopNodesPerCapacity implements OnInit { if (!this.widget) { this.seoService.setTitle($localize`:@@2d9883d230a47fbbb2ec969e32a186597ea27405:Liquidity Ranking`); + this.seoService.setDescription($localize`:@@meta.description.lightning.ranking.liquidity:See Lightning nodes with the most BTC liquidity deployed along with high-level stats like number of open channels, location, node age, and more.`); } for (let i = 1; i <= (this.widget ? 6 : 100); ++i) { diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts index 607ec2a99..3de177cc7 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts @@ -35,6 +35,7 @@ export class TopNodesPerChannels implements OnInit { if (this.widget === false) { this.seoService.setTitle($localize`:@@c50bf442cf99f6fc5f8b687c460f33234b879869:Connectivity Ranking`); + this.seoService.setDescription($localize`:@@meta.description.lightning.ranking.channels:See Lightning nodes with the most channels open along with high-level stats like total node capacity, node age, and more.`); this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$().pipe( map((ranking) => { diff --git a/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts b/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts index 90342c557..46becb793 100644 --- a/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts +++ b/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts @@ -20,6 +20,7 @@ export class NodesRankingsDashboard implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`Top lightning nodes`); + this.seoService.setDescription($localize`:@@meta.description.lightning.rankings-dashboard:See top the Lightning network nodes ranked by liquidity, connectivity, and age.`); this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share()); } } diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts index dd034eabd..41e170de6 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts @@ -64,6 +64,7 @@ export class LightningStatisticsChartComponent implements OnInit { this.miningWindowPreference = '3y'; } else { this.seoService.setTitle($localize`:@@ea8db27e6db64f8b940711948c001a1100e5fe9f:Lightning Network Capacity`); + this.seoService.setDescription($localize`:@@meta.description.lightning.stats-chart:See the capacity of the Lightning network visualized over time in terms of the number of open channels and total bitcoin capacity.`); this.miningWindowPreference = this.miningService.getDefaultTimespan('all'); } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 7d206f4b5..e6b43cdd7 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -135,4 +135,13 @@ export function haversineDistance(lat1: number, lon1: number, lat2: number, lon2 export function kmToMiles(km: number): number { return km * 0.62137119; -} \ No newline at end of file +} + +export function seoDescriptionNetwork(network: string): string { + if( network === 'liquidtestnet' || network === 'testnet' ) { + return ' Testnet'; + } else if( network === 'signet' || network === 'testnet' ) { + return ' ' + network.charAt(0).toUpperCase() + network.slice(1); + } + return ''; +} From d691bf2714e204e352686fe770d6430f61244a54 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 30 Aug 2023 23:59:51 +0900 Subject: [PATCH 5/5] Add meta titles+descriptions to pages missing them --- .../components/blocks-list/blocks-list.component.ts | 13 ++++++++++++- .../privacy-policy/privacy-policy.component.ts | 7 +++++++ .../push-transaction/push-transaction.component.ts | 8 ++++++++ .../app/components/rbf-list/rbf-list.component.ts | 8 +++++++- .../terms-of-service/terms-of-service.component.ts | 7 +++++++ .../trademark-policy/trademark-policy.component.ts | 7 +++++++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index cec925270..aa361d9dc 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -5,6 +5,8 @@ import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; import { WebsocketService } from '../../services/websocket.service'; +import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ selector: 'app-blocks-list', @@ -35,6 +37,7 @@ export class BlocksList implements OnInit { private websocketService: WebsocketService, public stateService: StateService, private cd: ChangeDetectorRef, + private seoService: SeoService, ) { } @@ -50,6 +53,14 @@ export class BlocksList implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; + this.seoService.setTitle($localize`:@@meta.title.blocks-list:Blocks`); + if( this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.liquid.blocks:See the most recent Liquid${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block size, and more.`); + } else { + this.seoService.setDescription($localize`:@@meta.description.bitcoin.blocks:See the most recent Bitcoin${seoDescriptionNetwork(this.stateService.network)} blocks along with basic stats such as block height, block reward, block size, and more.`); + } + + this.blocks$ = combineLatest([ this.fromHeightSubject.pipe( switchMap((fromBlockHeight) => { @@ -129,4 +140,4 @@ export class BlocksList implements OnInit { isEllipsisActive(e): boolean { return (e.offsetWidth < e.scrollWidth); } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts index f84903043..7a44070eb 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; @Component({ selector: 'app-privacy-policy', @@ -11,5 +12,11 @@ export class PrivacyPolicyComponent { constructor( private stateService: StateService, + private seoService: SeoService, ) { } + + ngOnInit(): void { + this.seoService.setTitle('Privacy Policy'); + this.seoService.setDescription('Trusted third parties are security holes, as are trusted first parties...you should only trust your own self-hosted instance of The Mempool Open Source Project™.'); + } } diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.ts b/frontend/src/app/components/push-transaction/push-transaction.component.ts index 8ee2af3f7..cbc5d905a 100644 --- a/frontend/src/app/components/push-transaction/push-transaction.component.ts +++ b/frontend/src/app/components/push-transaction/push-transaction.component.ts @@ -1,6 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { ApiService } from '../../services/api.service'; +import { StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ selector: 'app-push-transaction', @@ -16,12 +19,17 @@ export class PushTransactionComponent implements OnInit { constructor( private formBuilder: UntypedFormBuilder, private apiService: ApiService, + public stateService: StateService, + private seoService: SeoService, ) { } ngOnInit(): void { this.pushTxForm = this.formBuilder.group({ txHash: ['', Validators.required], }); + + this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`); + this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`); } postTx() { diff --git a/frontend/src/app/components/rbf-list/rbf-list.component.ts b/frontend/src/app/components/rbf-list/rbf-list.component.ts index b6e178270..1ae14702b 100644 --- a/frontend/src/app/components/rbf-list/rbf-list.component.ts +++ b/frontend/src/app/components/rbf-list/rbf-list.component.ts @@ -6,6 +6,8 @@ import { WebsocketService } from '../../services/websocket.service'; import { RbfTree } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; @Component({ selector: 'app-rbf-list', @@ -26,6 +28,7 @@ export class RbfList implements OnInit, OnDestroy { private apiService: ApiService, public stateService: StateService, private websocketService: WebsocketService, + private seoService: SeoService, ) { } ngOnInit(): void { @@ -51,9 +54,12 @@ export class RbfList implements OnInit, OnDestroy { this.isLoading = false; }) ); + + this.seoService.setTitle($localize`:@@meta.title.rbf-list:RBF Replacements`); + this.seoService.setDescription($localize`:@@meta.description.rbf-list:See the most recent RBF replacements on the Bitcoin${seoDescriptionNetwork(this.stateService.network)} network, updated in real-time.`); } ngOnDestroy(): void { this.websocketService.stopTrackRbf(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts index 66367dc49..708ebad76 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; @Component({ selector: 'app-terms-of-service', @@ -10,5 +11,11 @@ export class TermsOfServiceComponent { constructor( private stateService: StateService, + private seoService: SeoService, ) { } + + ngOnInit(): void { + this.seoService.setTitle('Terms of Service'); + this.seoService.setDescription('Out of respect for the Bitcoin community, the mempool.space website is Bitcoin Only and does not display any advertising.'); + } } diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts index 8a671c2fa..08f16264a 100644 --- a/frontend/src/app/components/trademark-policy/trademark-policy.component.ts +++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; @Component({ selector: 'app-trademark-policy', @@ -11,5 +12,11 @@ export class TrademarkPolicyComponent { constructor( private stateService: StateService, + private seoService: SeoService, ) { } + + ngOnInit(): void { + this.seoService.setTitle('Trademark Policy'); + this.seoService.setDescription('An overview of the trademarks registered by Mempool Space K.K. and The Mempool Open Source Project™ and what we consider to be lawful usage of those trademarks.'); + } }