2022-04-05 20:37:18 +02:00
import { ChangeDetectionStrategy , Component , Input , NgZone , OnInit , HostBinding } from '@angular/core' ;
2022-11-28 11:55:23 +09:00
import { UntypedFormBuilder , UntypedFormGroup } from '@angular/forms' ;
2022-06-15 11:26:58 +02:00
import { ActivatedRoute , Router } from '@angular/router' ;
2023-11-06 18:19:54 +00:00
import { EChartsOption , PieSeriesOption } from '../../graphs/echarts' ;
2023-03-08 02:14:41 -06:00
import { merge , Observable } from 'rxjs' ;
2022-06-01 14:07:01 +02:00
import { map , share , startWith , switchMap , tap } from 'rxjs/operators' ;
2022-09-21 17:23:45 +02:00
import { SeoService } from '../../services/seo.service' ;
2022-01-21 11:17:36 +09:00
import { StorageService } from '../..//services/storage.service' ;
2022-01-18 17:37:04 +09:00
import { MiningService , MiningStats } from '../../services/mining.service' ;
2022-01-21 11:17:36 +09:00
import { StateService } from '../../services/state.service' ;
2022-09-21 17:23:45 +02:00
import { chartColors , poolsColor } from '../../app.constants' ;
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe' ;
import { download } from '../../shared/graphs.utils' ;
import { isMobile } from '../../shared/common.utils' ;
2022-01-06 19:59:33 +09:00
@Component ( {
selector : 'app-pool-ranking' ,
templateUrl : './pool-ranking.component.html' ,
2022-01-21 21:50:57 +09:00
styleUrls : [ './pool-ranking.component.scss' ] ,
2022-02-24 20:20:18 +09:00
changeDetection : ChangeDetectionStrategy.OnPush ,
2022-01-06 19:59:33 +09:00
} )
2022-02-16 21:20:28 +09:00
export class PoolRankingComponent implements OnInit {
2024-02-07 23:26:06 +00:00
@Input ( ) height : number = 300 ;
2022-04-11 18:17:36 +09:00
@Input ( ) widget = false ;
2022-02-16 17:32:12 +09:00
2022-04-11 18:17:36 +09:00
miningWindowPreference : string ;
2022-11-28 11:55:23 +09:00
radioGroupForm : UntypedFormGroup ;
2022-01-14 18:09:40 +09:00
2023-02-20 17:47:45 +09:00
auditAvailable = false ;
indexingAvailable = false ;
2022-01-14 18:09:40 +09:00
isLoading = true ;
chartOptions : EChartsOption = { } ;
chartInitOptions = {
2022-02-22 20:15:15 +09:00
renderer : 'svg' ,
2022-01-14 18:09:40 +09:00
} ;
2022-05-05 16:18:28 +09:00
timespan = '' ;
2022-02-14 13:21:35 +09:00
chartInstance : any = undefined ;
2022-01-06 19:59:33 +09:00
2022-04-05 20:37:18 +02:00
@HostBinding ( 'attr.dir' ) dir = 'ltr' ;
2022-01-21 11:17:36 +09:00
miningStatsObservable$ : Observable < MiningStats > ;
2022-01-06 19:59:33 +09:00
constructor (
2023-11-02 01:29:55 +00:00
public stateService : StateService ,
2022-01-06 19:59:33 +09:00
private storageService : StorageService ,
2022-11-28 11:55:23 +09:00
private formBuilder : UntypedFormBuilder ,
2022-01-14 18:09:40 +09:00
private miningService : MiningService ,
2022-02-02 18:46:06 +09:00
private seoService : SeoService ,
2022-02-14 13:21:35 +09:00
private router : Router ,
2022-03-07 18:42:47 +01:00
private zone : NgZone ,
2022-06-15 11:26:58 +02:00
private route : ActivatedRoute ,
2022-01-14 18:09:40 +09:00
) {
2022-01-17 12:54:56 +09:00
}
2022-01-06 19:59:33 +09:00
2022-01-17 12:54:56 +09:00
ngOnInit ( ) : void {
2022-02-16 17:32:12 +09:00
if ( this . widget ) {
2022-04-11 18:17:36 +09:00
this . miningWindowPreference = '1w' ;
2022-02-16 17:32:12 +09:00
} else {
2022-02-24 20:20:18 +09:00
this . seoService . setTitle ( $localize ` :@@mining.mining-pools:Mining Pools ` ) ;
2023-08-30 20:26:07 +09:00
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. ` ) ;
2022-04-11 18:17:36 +09:00
this . miningWindowPreference = this . miningService . getDefaultTimespan ( '24h' ) ;
2022-02-16 17:32:12 +09:00
}
2022-04-11 18:17:36 +09:00
this . radioGroupForm = this . formBuilder . group ( { dateSpan : this.miningWindowPreference } ) ;
this . radioGroupForm . controls . dateSpan . setValue ( this . miningWindowPreference ) ;
2022-02-16 17:32:12 +09:00
2023-02-20 17:47:45 +09:00
this . indexingAvailable = ( this . stateService . env . BASE_MODULE === 'mempool' &&
this . stateService . env . MINING_DASHBOARD === true ) ;
this . auditAvailable = this . indexingAvailable && this . stateService . env . AUDIT ;
2022-06-15 11:26:58 +02:00
this . route
. fragment
. subscribe ( ( fragment ) = > {
if ( [ '24h' , '3d' , '1w' , '1m' , '3m' , '6m' , '1y' , '2y' , '3y' , 'all' ] . indexOf ( fragment ) > - 1 ) {
2022-06-23 15:30:42 +02:00
this . radioGroupForm . controls . dateSpan . setValue ( fragment , { emitEvent : false } ) ;
2022-06-15 11:26:58 +02:00
}
} ) ;
2023-03-08 02:14:41 -06:00
this . miningStatsObservable $ = merge (
2022-01-21 11:17:36 +09:00
this . radioGroupForm . get ( 'dateSpan' ) . valueChanges
. pipe (
2022-06-15 11:26:58 +02:00
startWith ( this . radioGroupForm . controls . dateSpan . value ) , // (trigger when the page loads)
2022-01-21 11:17:36 +09:00
tap ( ( value ) = > {
2023-02-21 18:48:09 +09:00
this . isLoading = true ;
2022-05-05 16:18:28 +09:00
this . timespan = value ;
2022-02-16 17:32:12 +09:00
if ( ! this . widget ) {
2022-04-11 18:17:36 +09:00
this . storageService . setValue ( 'miningWindowPreference' , value ) ;
2022-02-16 17:32:12 +09:00
}
2022-04-11 18:17:36 +09:00
this . miningWindowPreference = value ;
2022-06-01 14:07:01 +02:00
} ) ,
switchMap ( ( ) = > {
return this . miningService . getMiningStats ( this . miningWindowPreference ) ;
2022-01-21 11:17:36 +09:00
} )
2022-06-01 14:07:01 +02:00
) ,
2023-03-08 02:14:41 -06:00
this . stateService . chainTip $
2022-06-01 14:07:01 +02:00
. pipe (
switchMap ( ( ) = > {
return this . miningService . getMiningStats ( this . miningWindowPreference ) ;
} )
)
)
2022-01-21 11:17:36 +09:00
. pipe (
2022-01-21 21:50:57 +09:00
map ( data = > {
2022-03-05 18:47:21 +01:00
data [ 'minersLuck' ] = ( 100 * ( data . blockCount / 1008 ) ) . toFixed ( 2 ) ; // luck 1w
2022-01-21 21:50:57 +09:00
return data ;
} ) ,
2022-01-21 11:17:36 +09:00
tap ( data = > {
this . isLoading = false ;
this . prepareChartOptions ( data ) ;
2022-01-25 18:33:46 +09:00
} ) ,
share ( )
2022-01-21 11:17:36 +09:00
) ;
2022-01-06 19:59:33 +09:00
}
2022-01-21 11:17:36 +09:00
generatePoolsChartSerieData ( miningStats ) {
2022-08-13 10:40:46 +02:00
let poolShareThreshold = 0.5 ;
if ( isMobile ( ) ) {
poolShareThreshold = 2 ;
} else if ( this . widget ) {
poolShareThreshold = 1 ;
}
2023-08-30 20:26:07 +09:00
2022-01-14 18:09:40 +09:00
const data : object [ ] = [ ] ;
2022-03-01 18:29:02 +09:00
let totalShareOther = 0 ;
let totalBlockOther = 0 ;
let totalEstimatedHashrateOther = 0 ;
let edgeDistance : any = '20%' ;
2022-08-13 10:40:46 +02:00
if ( isMobile ( ) && this . widget ) {
2022-03-01 18:29:02 +09:00
edgeDistance = 0 ;
2022-08-13 10:40:46 +02:00
} else if ( isMobile ( ) && ! this . widget || this . widget ) {
2022-03-14 18:06:54 +01:00
edgeDistance = 10 ;
2022-03-01 18:29:02 +09:00
}
2022-01-14 18:09:40 +09:00
2022-01-21 11:17:36 +09:00
miningStats . pools . forEach ( ( pool ) = > {
2022-01-14 18:09:40 +09:00
if ( parseFloat ( pool . share ) < poolShareThreshold ) {
2022-03-01 18:29:02 +09:00
totalShareOther += parseFloat ( pool . share ) ;
totalBlockOther += pool . blockCount ;
totalEstimatedHashrateOther += pool . lastEstimatedHashrate ;
2022-01-14 18:09:40 +09:00
return ;
2022-01-06 19:59:33 +09:00
}
2022-02-17 09:41:05 +09:00
data . push ( {
2022-02-24 17:28:16 +09:00
itemStyle : {
2022-03-01 18:29:02 +09:00
color : poolsColor [ pool . name . replace ( /[^a-zA-Z0-9]/g , '' ) . toLowerCase ( ) ] ,
2022-02-24 17:28:16 +09:00
} ,
2022-01-17 15:34:34 +09:00
value : pool.share ,
2022-08-13 10:40:46 +02:00
name : pool.name + ( ( isMobile ( ) || this . widget ) ? ` ` : ` ( ${ pool . share } %) ` ) ,
2022-01-21 21:50:57 +09:00
label : {
2022-03-01 18:29:02 +09:00
overflow : 'none' ,
2023-01-03 05:24:14 -06:00
color : 'var(--tooltip-grey)' ,
2022-03-01 18:29:02 +09:00
alignTo : 'edge' ,
edgeDistance : edgeDistance ,
2022-01-21 21:50:57 +09:00
} ,
2022-01-14 18:09:40 +09:00
tooltip : {
2022-08-13 10:40:46 +02:00
show : ! isMobile ( ) || ! this . widget ,
2022-02-21 23:36:05 +09:00
backgroundColor : 'rgba(17, 19, 31, 1)' ,
borderRadius : 4 ,
shadowColor : 'rgba(0, 0, 0, 0.5)' ,
2022-01-21 21:50:57 +09:00
textStyle : {
2023-01-03 05:24:14 -06:00
color : 'var(--tooltip-grey)' ,
2022-01-21 21:50:57 +09:00
} ,
2022-02-21 23:36:05 +09:00
borderColor : '#000' ,
2022-01-14 18:09:40 +09:00
formatter : ( ) = > {
2022-05-18 03:06:31 +04:00
const i = pool . blockCount . toString ( ) ;
2022-04-11 18:17:36 +09:00
if ( this . miningWindowPreference === '24h' ) {
2022-02-21 23:36:05 +09:00
return ` <b style="color: white"> ${ pool . name } ( ${ pool . share } %)</b><br> ` +
2024-02-16 09:59:37 +01:00
pool . lastEstimatedHashrate . toString ( ) + ' ' + miningStats . miningUnits . hashrateUnit +
2023-03-13 17:40:17 +09:00
` <br> ` + $localize ` ${ i } :INTERPOLATION: blocks ` ;
2022-01-18 17:37:04 +09:00
} else {
2022-02-21 23:36:05 +09:00
return ` <b style="color: white"> ${ pool . name } ( ${ pool . share } %)</b><br> ` +
2023-03-13 17:40:17 +09:00
$localize ` ${ i } :INTERPOLATION: blocks ` ;
2022-01-18 17:37:04 +09:00
}
2022-01-14 18:09:40 +09:00
}
2022-02-14 13:21:35 +09:00
} ,
2022-03-25 14:22:22 +09:00
data : pool.slug ,
2022-02-17 09:41:05 +09:00
} as PieSeriesOption ) ;
2022-01-06 19:59:33 +09:00
} ) ;
2022-03-01 18:29:02 +09:00
2024-04-04 21:55:14 +09:00
const percentage = totalShareOther . toFixed ( 2 ) + '%' ;
2022-03-01 18:29:02 +09:00
// 'Other'
data . push ( {
itemStyle : {
2023-02-19 17:14:49 +09:00
color : '#6b6b6b' ,
2022-03-01 18:29:02 +09:00
} ,
value : totalShareOther ,
2024-04-04 21:55:14 +09:00
name : $localize ` Other ( ${ percentage } ) ` ,
2022-03-01 18:29:02 +09:00
label : {
overflow : 'none' ,
2023-01-03 05:24:14 -06:00
color : 'var(--tooltip-grey)' ,
2022-03-01 18:29:02 +09:00
alignTo : 'edge' ,
edgeDistance : edgeDistance
} ,
tooltip : {
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-01 18:29:02 +09:00
} ,
borderColor : '#000' ,
formatter : ( ) = > {
2023-03-13 17:40:17 +09:00
const i = totalBlockOther . toString ( ) ;
2022-04-11 18:17:36 +09:00
if ( this . miningWindowPreference === '24h' ) {
2023-03-13 17:08:27 +09:00
return ` <b style="color: white"> ` + $localize ` Other ( ${ percentage } ) ` + ` </b><br> ` +
2024-02-16 09:59:37 +01:00
totalEstimatedHashrateOther . toString ( ) + ' ' + miningStats . miningUnits . hashrateUnit +
2023-03-13 17:40:17 +09:00
` <br> ` + $localize ` ${ i } :INTERPOLATION: blocks ` ;
2022-03-01 18:29:02 +09:00
} else {
2023-03-13 17:08:27 +09:00
return ` <b style="color: white"> ` + $localize ` Other ( ${ percentage } ) ` + ` </b><br> ` +
2023-03-13 17:40:17 +09:00
$localize ` ${ i } :INTERPOLATION: blocks ` ;
2022-03-01 18:29:02 +09:00
}
}
} ,
data : 9999 as any ,
} as PieSeriesOption ) ;
2022-01-14 18:09:40 +09:00
return data ;
}
2022-01-06 19:59:33 +09:00
2022-01-21 11:17:36 +09:00
prepareChartOptions ( miningStats ) {
2022-06-14 09:42:56 +02:00
let pieSize = [ '20%' , '80%' ] ; // Desktop
2022-08-13 10:40:46 +02:00
if ( isMobile ( ) && ! this . widget ) {
2022-06-14 09:42:56 +02:00
pieSize = [ '15%' , '60%' ] ;
}
2022-01-14 18:09:40 +09:00
this . chartOptions = {
2022-03-14 18:06:54 +01:00
animation : false ,
2023-02-22 14:06:08 +09:00
color : chartColors.filter ( color = > color !== '#FDD835' ) ,
2022-01-14 18:09:40 +09:00
tooltip : {
2022-02-22 20:30:14 +09:00
trigger : 'item' ,
textStyle : {
align : 'left' ,
}
2022-01-14 18:09:40 +09:00
} ,
series : [
{
2022-04-11 21:17:15 +09:00
zlevel : 0 ,
2022-08-13 10:40:46 +02:00
minShowLabelAngle : 1.8 ,
2022-01-14 18:09:40 +09:00
name : 'Mining pool' ,
type : 'pie' ,
2022-06-14 09:42:56 +02:00
radius : pieSize ,
2022-01-21 11:17:36 +09:00
data : this.generatePoolsChartSerieData ( miningStats ) ,
2022-01-14 18:09:40 +09:00
labelLine : {
lineStyle : {
width : 2 ,
} ,
} ,
label : {
fontSize : 14 ,
2022-06-14 09:42:56 +02:00
formatter : ( serie ) = > ` ${ serie . name === 'Binance Pool' ? 'Binance\nPool' : serie . name } ` ,
2022-01-14 18:09:40 +09:00
} ,
itemStyle : {
2022-03-09 16:30:45 +01:00
borderRadius : 1 ,
borderWidth : 1 ,
2022-01-14 18:09:40 +09:00
borderColor : '#000' ,
} ,
emphasis : {
itemStyle : {
2022-02-17 17:52:12 +09:00
shadowBlur : 40 ,
shadowColor : 'rgba(0, 0, 0, 0.75)' ,
2022-01-14 18:09:40 +09:00
} ,
labelLine : {
lineStyle : {
width : 3 ,
}
}
}
}
2022-02-17 17:52:12 +09:00
] ,
2022-01-14 18:09:40 +09:00
} ;
}
2022-02-14 13:21:35 +09:00
onChartInit ( ec ) {
if ( this . chartInstance !== undefined ) {
return ;
}
this . chartInstance = ec ;
this . chartInstance . on ( 'click' , ( e ) = > {
2022-03-01 18:29:02 +09:00
if ( e . data . data === 9999 ) { // "Other"
return ;
}
2022-03-07 18:42:47 +01:00
this . zone . run ( ( ) = > {
2022-03-25 10:57:34 +09:00
const url = new RelativeUrlPipe ( this . stateService ) . transform ( ` /mining/pool/ ${ e . data . data } ` ) ;
this . router . navigate ( [ url ] ) ;
2022-03-07 18:42:47 +01:00
} ) ;
2022-02-17 09:41:05 +09:00
} ) ;
2022-02-14 13:21:35 +09:00
}
2022-02-17 09:41:05 +09:00
2022-01-21 11:17:36 +09:00
/ * *
* Default mining stats if something goes wrong
* /
2022-03-05 18:47:21 +01:00
getEmptyMiningStat ( ) : MiningStats {
2022-01-21 11:17:36 +09:00
return {
lastEstimatedHashrate : 'Error' ,
blockCount : 0 ,
totalEmptyBlock : 0 ,
totalEmptyBlockRatio : '' ,
pools : [ ] ,
2022-04-15 20:43:10 +09:00
totalBlockCount : 0 ,
2022-01-21 11:17:36 +09:00
miningUnits : {
hashrateDivider : 1 ,
hashrateUnit : '' ,
} ,
} ;
}
2022-05-05 16:18:28 +09:00
onSaveChart() {
const now = new Date ( ) ;
2024-04-04 15:36:24 +09:00
this . chartOptions . backgroundColor = 'var(--active-bg)' ;
2022-05-09 11:01:51 +02:00
this . chartInstance . setOption ( this . chartOptions ) ;
2022-05-05 16:18:28 +09:00
download ( this . chartInstance . getDataURL ( {
pixelRatio : 2 ,
excludeComponents : [ 'dataZoom' ] ,
2022-05-09 11:01:51 +02:00
} ) , ` pools-ranking- ${ this . timespan } - ${ Math . round ( now . getTime ( ) / 1000 ) } .svg ` ) ;
this . chartOptions . backgroundColor = 'none' ;
this . chartInstance . setOption ( this . chartOptions ) ;
2022-05-05 16:18:28 +09:00
}
2022-06-09 15:58:49 +02:00
isEllipsisActive ( e ) {
return ( e . offsetWidth < e . scrollWidth ) ;
}
2022-01-06 19:59:33 +09:00
}