import { AfterContentInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , HostListener , OnDestroy , ViewRef , } from '@angular/core' ; import { MmrPercentile } from '@firestone-hs/bgs-global-stats' ; import { Subscription } from 'rxjs' ; import { distinctUntilChanged , filter , takeUntil , tap } from 'rxjs/operators' ; import { GroupedReplays } from '../../../../models/mainwindow/replays/grouped-replays' ; import { GameStat } from '../../../../models/mainwindow/stats/game-stat' ; import { LocalizationFacadeService } from '../../../../services/localization-facade.service' ; import { AppUiStoreFacadeService } from '../../../../services/ui-store/app-ui-store-facade.service' ; import { cdLog } from '../../../../services/ui-store/app-ui-store.service' ; import { getMmrThreshold } from '../../../../services/ui-store/bgs-ui-helper' ; import { arraysEqual , groupByFunction } from '../../../../services/utils' ; import { AbstractSubscriptionComponent } from '../../../abstract-subscription.component' ; @ Component ( { selector : 'battlegrounds-perfect-games' , styleUrls : [ `../../../../../css/global/components-global.scss` , `../../../../../css/component/battlegrounds/desktop/categories/battlegrounds-perfect-games.component.scss` , ] , template : `
		<div class="battlegrounds-perfect-games">
			<infinite-scroll *ngIf="allReplays?.length" class="replays-list" (scrolled)="onScroll()" scrollable>
				<li *ngFor="let groupedReplay of displayedGroupedReplays" class="grouped-replays">
					<grouped-replays [groupedReplays]="groupedReplay"></grouped-replays>
				</li>
				<div class="loading" *ngIf="isLoading" (click)="onScroll()">Click to load more replays</div>
			</infinite-scroll>
		</div>
	` , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class BattlegroundsPerfectGamesComponent extends AbstractSubscriptionComponent implements AfterContentInit , OnDestroy { isLoading : boolean ; allReplays : readonly GameStat [ ] ; displayedGroupedReplays : readonly GroupedReplays [ ] = [ ] ; private sub$$ : Subscription ; private displayedReplays : readonly GameStat [ ] = [ ] ; private gamesIterator : IterableIterator < void > ; constructor ( protected readonly store , protected readonly cdr , private i18n ) { super ( store , cdr ) ; } ngAfterContentInit ( ) { this . sub$$ = this . store . listen$ ( ( [ main , nav ] ) => main . battlegrounds . perfectGames , ( [ main , nav ] ) => main . battlegrounds . globalStats . mmrPercentiles , ( [ main , nav , prefs ] ) => prefs . bgsActiveRankFilter , ( [ main , nav , prefs ] ) => prefs . bgsActiveHeroFilter ) . pipe ( filter ( ( [ perfectGames , mmrPercentiles , rankFilter , heroFilter ] ) => ! ! perfectGames ?. length ) , distinctUntilChanged ( ( a , b ) => this . areEqual ( a , b ) ) , tap ( ( stat ) => cdLog ( 'emitting in ' , this . constructor . name , stat ) ) , takeUntil ( this . destroyed$ ) ) . subscribe ( ( [ gameStats , mmrPercentiles , rankFilter , heroFilter ] ) => { setTimeout ( ( ) => { this . displayedReplays = [ ] ; this . displayedGroupedReplays = [ ] ; const mmrThreshold = getMmrThreshold ( rankFilter , mmrPercentiles ) ; this . gamesIterator = this . buildIterator ( gameStats , mmrThreshold , heroFilter , 8 ) ; this . onScroll ( ) ; } ) ; } ) ; } @ HostListener ( 'window:beforeunload' ) ngOnDestroy ( ) { super . ngOnDestroy ( ) ; this . sub$$ ?. unsubscribe ( ) ; } onScroll ( ) { this . gamesIterator && this . gamesIterator . next ( ) ; } private * buildIterator ( perfectGames , rankFilter , heroFilter , step = 40 ) { this . allReplays = this . applyFilters ( perfectGames ?? [ ] , rankFilter , heroFilter ) ; const workingReplays = [ ... this . allReplays ] ; while ( workingReplays . length > 0 ) { const currentReplays = [ ] ; while ( workingReplays . length > 0 && currentReplays . length < step ) { currentReplays . push ( ... workingReplays . splice ( 0 , 1 ) ) ; } this . displayedReplays = [ ... this . displayedReplays , ... currentReplays ] ; this . displayedGroupedReplays = this . groupReplays ( this . displayedReplays ) ; this . isLoading = this . allReplays . length > step ; if ( ! ( this . cdr as ViewRef ) ?. destroyed ) { this . cdr . detectChanges ( ) ; } yield ; } this . isLoading = false ; if ( ! ( this . cdr as ViewRef ) ?. destroyed ) { this . cdr . detectChanges ( ) ; } return ; } private areEqual ( a , b ) { if ( a [ 2 ] !== b [ 2 ] || a [ 3 ] !== b [ 3 ] ) { return false ; } return arraysEqual ( a [ 0 ] , b [ 0 ] ) && arraysEqual ( a [ 1 ] , b [ 1 ] ) ; } private applyFilters ( replays , rankFilter , heroFilter ) { return replays . filter ( ( replay ) => this . rankFilter ( replay , rankFilter ) ) . filter ( ( replay ) => this . heroFilter ( replay , heroFilter ) ) ; } private rankFilter ( stat , rankFilter ) { if ( ! rankFilter ) { return true ; } return stat . playerRank && parseInt ( stat . playerRank ) >= rankFilter ; } private heroFilter ( stat , heroFilter ) { if ( ! heroFilter ) { return true ; } switch ( heroFilter ) { case 'all' : case null : return true ; default : return stat . playerCardId === heroFilter ; } } private groupReplays ( replays ) { const groupingFunction = ( replay ) => { const date = new Date ( replay . creationTimestamp ) ; return date . toLocaleDateString ( this . i18n . formatCurrentLocale ( ) , { month : 'short' , day : '2-digit' , year : 'numeric' , } ) ; } ; const groupByDate = groupByFunction ( groupingFunction ) ; const replaysByDate = groupByDate ( replays ) ; return Object . keys ( replaysByDate ) . map ( ( date ) => ( { header : date , replays : replaysByDate [ date ] , } ) ) ; } }