2022-03-08 12:50:47 +01:00
import { ChangeDetectionStrategy , Component , Inject , Input , LOCALE_ID , OnInit } from '@angular/core' ;
2022-02-09 19:41:05 +09:00
import { ActivatedRoute } from '@angular/router' ;
2023-11-06 18:19:54 +00:00
import { echarts , EChartsOption } from '../../graphs/echarts' ;
2024-03-15 07:41:07 +00:00
import { BehaviorSubject , Observable , combineLatest , of , timer } from 'rxjs' ;
import { catchError , distinctUntilChanged , filter , map , share , switchMap , tap } from 'rxjs/operators' ;
2022-09-21 17:23:45 +02:00
import { BlockExtended , PoolStat } from '../../interfaces/node-api.interface' ;
import { ApiService } from '../../services/api.service' ;
import { StateService } from '../../services/state.service' ;
import { selectPowerOfTen } from '../../bitcoin.utils' ;
2022-03-08 12:50:47 +01:00
import { formatNumber } from '@angular/common' ;
2022-09-21 17:23:45 +02:00
import { SeoService } from '../../services/seo.service' ;
2022-02-08 18:56:51 +09:00
2024-03-15 07:41:07 +00:00
interface AccelerationTotal {
cost : number ,
count : number ,
}
2022-02-08 18:56:51 +09:00
@Component ( {
selector : 'app-pool' ,
templateUrl : './pool.component.html' ,
2022-02-15 18:36:58 +09:00
styleUrls : [ './pool.component.scss' ] ,
changeDetection : ChangeDetectionStrategy.OnPush
2022-02-08 18:56:51 +09:00
} )
export class PoolComponent implements OnInit {
2022-03-08 12:50:47 +01:00
@Input ( ) right : number | string = 45 ;
@Input ( ) left : number | string = 75 ;
2022-03-25 17:48:24 +09:00
gfg = true ;
2022-03-30 18:43:01 +09:00
2022-03-09 16:30:45 +01:00
formatNumber = formatNumber ;
2022-02-09 19:41:05 +09:00
poolStats$ : Observable < PoolStat > ;
2022-02-10 19:16:00 +09:00
blocks$ : Observable < BlockExtended [ ] > ;
2024-03-15 07:41:07 +00:00
oobFees$ : Observable < AccelerationTotal [ ] > ;
2022-03-08 12:50:47 +01:00
isLoading = true ;
chartOptions : EChartsOption = { } ;
chartInitOptions = {
renderer : 'svg' ,
} ;
2022-02-09 19:41:05 +09:00
2022-02-10 19:16:00 +09:00
blocks : BlockExtended [ ] = [ ] ;
2022-03-25 14:22:22 +09:00
slug : string = undefined ;
2022-02-09 19:41:05 +09:00
2023-03-16 16:13:11 +09:00
auditAvailable = false ;
2022-03-30 19:02:05 +09:00
loadMoreSubject : BehaviorSubject < number > = new BehaviorSubject ( this . blocks [ this . blocks . length - 1 ] ? . height ) ;
2022-03-22 17:19:34 +09:00
2022-02-08 18:56:51 +09:00
constructor (
2022-03-08 12:50:47 +01:00
@Inject ( LOCALE_ID ) public locale : string ,
2022-02-09 19:41:05 +09:00
private apiService : ApiService ,
private route : ActivatedRoute ,
public stateService : StateService ,
2022-04-05 00:36:00 +09:00
private seoService : SeoService ,
2022-02-10 19:16:00 +09:00
) {
2023-03-16 16:13:11 +09:00
this . auditAvailable = this . stateService . env . AUDIT ;
2022-02-10 19:16:00 +09:00
}
2022-02-08 18:56:51 +09:00
ngOnInit ( ) : void {
2022-03-25 14:22:22 +09:00
this . poolStats $ = this . route . params . pipe ( map ( ( params ) = > params . slug ) )
2022-02-09 19:41:05 +09:00
. pipe (
2022-03-25 14:22:22 +09:00
switchMap ( ( slug : any ) = > {
2022-03-08 18:58:19 +01:00
this . isLoading = true ;
2022-03-25 14:22:22 +09:00
this . slug = slug ;
return this . apiService . getPoolHashrate $ ( this . slug )
2022-03-08 12:50:47 +01:00
. pipe (
switchMap ( ( data ) = > {
2022-03-08 20:29:09 +01:00
this . isLoading = false ;
2024-03-26 14:11:08 +09:00
const hashrate = data . map ( val = > [ val . timestamp * 1000 , val . avgHashrate ] ) ;
const share = data . map ( val = > [ val . timestamp * 1000 , val . share * 100 ] ) ;
this . prepareChartOptions ( hashrate , share ) ;
2022-03-30 18:43:01 +09:00
return [ slug ] ;
2022-03-08 12:50:47 +01:00
} ) ,
2023-03-09 02:34:21 -06:00
catchError ( ( ) = > {
this . isLoading = false ;
this . seoService . logSoft404 ( ) ;
return of ( [ slug ] ) ;
} )
2022-03-16 19:17:33 +01:00
) ;
2022-03-08 12:50:47 +01:00
} ) ,
2022-03-30 18:43:01 +09:00
switchMap ( ( slug ) = > {
2023-03-09 02:34:21 -06:00
return this . apiService . getPoolStats $ ( slug ) . pipe (
catchError ( ( ) = > {
this . isLoading = false ;
this . seoService . logSoft404 ( ) ;
return of ( null ) ;
} )
) ;
2022-02-09 19:41:05 +09:00
} ) ,
2022-03-30 19:02:05 +09:00
tap ( ( ) = > {
2023-07-08 01:07:06 -04:00
this . loadMoreSubject . next ( this . blocks [ 0 ] ? . height ) ;
2022-03-30 19:02:05 +09:00
} ) ,
2022-02-11 18:29:38 +09:00
map ( ( poolStats ) = > {
2022-04-05 00:36:00 +09:00
this . seoService . setTitle ( poolStats . pool . name ) ;
2023-08-30 20:26:07 +09:00
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. ` ) ;
2022-02-11 19:10:29 +09:00
let regexes = '"' ;
2022-02-15 18:45:53 +09:00
for ( const regex of poolStats . pool . regexes ) {
2022-02-11 19:10:29 +09:00
regexes += regex + '", "' ;
}
poolStats . pool . regexes = regexes . slice ( 0 , - 3 ) ;
2022-04-05 00:36:00 +09:00
2022-02-11 18:29:38 +09:00
return Object . assign ( {
2023-07-28 13:45:04 +09:00
logo : ` /resources/mining-pools/ ` + poolStats . pool . slug + '.svg'
2022-02-11 18:29:38 +09:00
} , poolStats ) ;
} )
2022-02-09 19:41:05 +09:00
) ;
2022-03-16 19:17:33 +01:00
this . blocks $ = this . loadMoreSubject
2022-02-10 19:16:00 +09:00
. pipe (
2022-03-30 19:02:05 +09:00
distinctUntilChanged ( ) ,
2022-03-16 19:17:33 +01:00
switchMap ( ( flag ) = > {
2022-03-25 14:22:22 +09:00
if ( this . slug === undefined ) {
2022-03-22 17:19:34 +09:00
return [ ] ;
}
2022-03-25 14:22:22 +09:00
return this . apiService . getPoolBlocks $ ( this . slug , this . blocks [ this . blocks . length - 1 ] ? . height ) ;
2022-02-10 19:16:00 +09:00
} ) ,
tap ( ( newBlocks ) = > {
this . blocks = this . blocks . concat ( newBlocks ) ;
} ) ,
2022-03-30 19:02:05 +09:00
map ( ( ) = > this . blocks ) ,
share ( ) ,
2022-03-16 19:17:33 +01:00
) ;
2024-03-15 07:41:07 +00:00
this . oobFees $ = this . route . params . pipe ( map ( ( params ) = > params . slug ) ) . pipe (
2024-04-02 08:23:38 +00:00
filter ( ( ) = > this . stateService . env . PUBLIC_ACCELERATIONS === true && this . stateService . network === '' ) ,
2024-03-15 07:41:07 +00:00
switchMap ( slug = > {
return combineLatest ( [
this . apiService . getAccelerationTotals $ ( this . slug , '1w' ) ,
this . apiService . getAccelerationTotals $ ( this . slug , '1m' ) ,
this . apiService . getAccelerationTotals $ ( this . slug ) ,
] ) ;
} ) ,
filter ( oob = > oob . length === 3 && oob [ 2 ] . count > 0 )
) ;
2022-02-10 19:16:00 +09:00
}
2022-02-09 19:41:05 +09:00
2024-03-26 14:11:08 +09:00
prepareChartOptions ( hashrate , share ) {
2022-04-05 00:36:00 +09:00
let title : object ;
2024-03-26 14:11:08 +09:00
if ( hashrate . length <= 1 ) {
2022-04-05 00:36:00 +09:00
title = {
textStyle : {
color : 'grey' ,
fontSize : 15
} ,
2023-12-08 17:00:36 +01:00
text : $localize ` Not enough data yet ` ,
2022-04-05 00:36:00 +09:00
left : 'center' ,
top : 'center'
} ;
}
2022-03-08 12:50:47 +01:00
this . chartOptions = {
2022-04-05 00:36:00 +09:00
title : title ,
2022-03-08 12:50:47 +01:00
animation : false ,
color : [
2023-11-06 18:19:54 +00:00
new echarts . graphic . LinearGradient ( 0 , 0 , 0 , 0.65 , [
2022-03-08 12:50:47 +01:00
{ offset : 0 , color : '#F4511E' } ,
{ offset : 0.25 , color : '#FB8C00' } ,
{ offset : 0.5 , color : '#FFB300' } ,
{ offset : 0.75 , color : '#FDD835' } ,
{ offset : 1 , color : '#7CB342' }
] ) ,
'#D81B60' ,
] ,
grid : {
right : this.right ,
left : this.left ,
bottom : 60 ,
} ,
tooltip : {
show : ! this . isMobile ( ) ,
trigger : 'axis' ,
axisPointer : {
type : 'line'
} ,
backgroundColor : 'rgba(17, 19, 31, 1)' ,
borderRadius : 4 ,
shadowColor : 'rgba(0, 0, 0, 0.5)' ,
textStyle : {
2023-01-03 05:24:14 -06:00
color : 'var(--tooltip-grey)' ,
2022-03-08 12:50:47 +01:00
align : 'left' ,
} ,
borderColor : '#000' ,
2022-03-30 18:43:01 +09:00
formatter : function ( ticks : any [ ] ) {
2024-03-26 14:11:08 +09:00
let hashrateString = '' ;
let dominanceString = '' ;
2022-03-08 12:50:47 +01:00
2024-03-26 14:11:08 +09:00
for ( const tick of ticks ) {
if ( tick . seriesIndex === 0 ) {
let hashratePowerOfTen = selectPowerOfTen ( tick . data [ 1 ] , 10 ) ;
let hashrateData = tick . data [ 1 ] / hashratePowerOfTen . divider ;
hashrateString = ` ${ tick . marker } ${ tick . seriesName } : ${ formatNumber ( hashrateData , this . locale , '1.0-0' ) } ${ hashratePowerOfTen . unit } H/s<br> ` ;
} else if ( tick . seriesIndex === 1 ) {
dominanceString = ` ${ tick . marker } ${ tick . seriesName } : ${ formatNumber ( tick . data [ 1 ] , this . locale , '1.0-2' ) } % ` ;
}
}
2022-03-08 12:50:47 +01:00
return `
2022-03-16 19:17:33 +01:00
< b style = "color: white; margin-left: 18px" > $ { ticks [ 0 ] . axisValueLabel } < / b > < br >
2024-03-26 14:11:08 +09:00
< span > $ { hashrateString } < / span >
< span > $ { dominanceString } < / span >
2022-03-08 12:50:47 +01:00
` ;
} . bind ( this )
} ,
2024-03-26 14:11:08 +09:00
xAxis : hashrate.length <= 1 ? undefined : {
2022-03-08 12:50:47 +01:00
type : 'time' ,
splitNumber : ( this . isMobile ( ) ) ? 5 : 10 ,
2022-04-11 21:17:15 +09:00
axisLabel : {
hideOverlap : true ,
}
2022-03-08 12:50:47 +01:00
} ,
2024-03-26 14:11:08 +09:00
legend : {
data : [
{
2024-04-03 18:13:42 +09:00
name : $localize ` :@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate ` ,
2024-03-26 14:11:08 +09:00
inactiveColor : 'rgb(110, 112, 121)' ,
textStyle : {
color : 'white' ,
} ,
icon : 'roundRect' ,
itemStyle : {
color : '#FFB300' ,
} ,
} ,
{
name : $localize ` :mining.pool-dominance:Pool Dominance ` ,
inactiveColor : 'rgb(110, 112, 121)' ,
textStyle : {
color : 'white' ,
} ,
icon : 'roundRect' ,
} ,
] ,
} ,
yAxis : hashrate.length <= 1 ? undefined : [
2022-03-08 12:50:47 +01:00
{
2022-03-16 17:00:06 +01:00
min : ( value ) = > {
2022-03-08 12:50:47 +01:00
return value . min * 0.9 ;
} ,
type : 'value' ,
axisLabel : {
color : 'rgb(110, 112, 121)' ,
formatter : ( val ) = > {
const selectedPowerOfTen : any = selectPowerOfTen ( val ) ;
const newVal = Math . round ( val / selectedPowerOfTen . divider ) ;
return ` ${ newVal } ${ selectedPowerOfTen . unit } H/s `
}
} ,
splitLine : {
show : false ,
}
} ,
2024-03-26 14:11:08 +09:00
{
type : 'value' ,
axisLabel : {
color : 'rgb(110, 112, 121)' ,
formatter : ( val ) = > {
return ` ${ val } % `
}
} ,
splitLine : {
show : false ,
}
}
2022-03-08 12:50:47 +01:00
] ,
2024-03-26 14:11:08 +09:00
series : hashrate.length <= 1 ? undefined : [
2022-03-08 12:50:47 +01:00
{
2024-03-26 14:11:08 +09:00
zlevel : 1 ,
2024-04-03 18:13:42 +09:00
name : $localize ` :@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate ` ,
2022-03-08 12:50:47 +01:00
showSymbol : false ,
symbol : 'none' ,
2024-03-26 14:11:08 +09:00
data : hashrate ,
2022-03-08 12:50:47 +01:00
type : 'line' ,
lineStyle : {
width : 2 ,
} ,
} ,
2024-03-26 14:11:08 +09:00
{
zlevel : 0 ,
name : $localize ` :mining.pool-dominance:Pool Dominance ` ,
showSymbol : false ,
symbol : 'none' ,
data : share ,
type : 'line' ,
yAxisIndex : 1 ,
lineStyle : {
width : 2 ,
} ,
}
2022-03-08 12:50:47 +01:00
] ,
2024-03-26 14:11:08 +09:00
dataZoom : hashrate.length <= 1 ? undefined : [ {
2022-03-29 18:20:00 +09:00
type : 'inside' ,
realtime : true ,
zoomLock : true ,
maxSpan : 100 ,
minSpan : 10 ,
moveOnMouseMove : false ,
} , {
fillerColor : '#aaaaff15' ,
borderColor : '#ffffff88' ,
showDetail : false ,
show : true ,
type : 'slider' ,
brushSelect : false ,
realtime : true ,
bottom : 0 ,
left : 20 ,
right : 15 ,
2022-03-30 18:43:01 +09:00
selectedDataBackground : {
2022-03-29 18:20:00 +09:00
lineStyle : {
color : '#fff' ,
opacity : 0.45 ,
} ,
areaStyle : {
opacity : 0 ,
2022-03-30 18:43:01 +09:00
} ,
2022-03-29 18:20:00 +09:00
} ,
} ] ,
2022-03-08 12:50:47 +01:00
} ;
}
isMobile() {
return ( window . innerWidth <= 767.98 ) ;
}
2022-02-10 19:16:00 +09:00
loadMore() {
2022-03-30 19:02:05 +09:00
this . loadMoreSubject . next ( this . blocks [ this . blocks . length - 1 ] ? . height ) ;
2022-02-09 19:41:05 +09:00
}
trackByBlock ( index : number , block : BlockExtended ) {
return block . height ;
2022-02-08 18:56:51 +09:00
}
}