import {Component, OnDestroy, OnInit} from '@angular/core'; import {environment} from "../../environments/environment"; import {SuspiciousScore} from "../replays"; import {Observable, Subscription, timer} from 'rxjs'; import {LocalCacheService} from "../../corelib/service/local-cache.service"; import {ActivatedRoute, Router, RouterLink} from "@angular/router"; import {FilterManagerService} from "../filter-manager.service"; import {FormsModule} from "@angular/forms"; import {DecimalPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common"; export interface SuspiciousScoresFilter { sorting?: string; minPP?: number; maxPP?: number; minUR?: number; maxUR?: number; searchUsername?: string; searchBeatmap?: string; } @Component({ selector: 'app-view-suspicious-scores', standalone: true, templateUrl: './view-suspicious-scores.component.html', imports: [ FormsModule, RouterLink, NgForOf, DecimalPipe, NgOptimizedImage, NgIf ], styleUrls: ['./view-suspicious-scores.component.css'] }) export class ViewSuspiciousScoresComponent implements OnInit, OnDestroy { filterManager = new FilterManagerService("suspiciousScoreFilters"); isUrlFilters = false; originalScores: SuspiciousScore[] = []; filteredScores: SuspiciousScore[] = []; currentPage = 1; itemsPerPage = 150; autoUpdateInterval: number = 3600000; isLoading: boolean = true; private updateSubscription: Subscription | null = null; constructor( private localCacheService: LocalCacheService, private router: Router, private activatedRoute: ActivatedRoute ) { } ngOnInit(): void { this.activatedRoute.params.subscribe(params => { let filters = params['f']; if (filters) { this.filterManager.setFiltersFromUrl(filters); this.isUrlFilters = true; } else { this.filterManager.setFiltersFromLocal(); } }); this.startAutoUpdateTimer(); } getTotalPages(): number { return Math.ceil(this.filteredScores.length / this.itemsPerPage); } getCurrentPage(): SuspiciousScore[] { const start = (this.currentPage - 1) * this.itemsPerPage; const end = start + this.itemsPerPage; return this.filteredScores.slice(start, end); } getFiltersUrl(): void { let targetUrl = '/sus/' + this.filterManager.getFiltersUrl(); this.router.navigate([targetUrl]).then(() => { navigator.clipboard.writeText(targetUrl).then(() => { console.debug('Loaded filters!') }); }); } ngOnDestroy(): void { if (this.updateSubscription) { this.updateSubscription.unsubscribe(); } } startAutoUpdateTimer(): void { this.updateSubscription = timer(0, this.autoUpdateInterval).subscribe(() => { this.isLoading = true; this.getSuspiciousScores().subscribe(scores => { this.originalScores = scores; this.filterScores(); this.isLoading = false; }); }); } resetValues(): void { this.filterManager.resetValues(); this.filterScores(); this.router.navigate(['/sus']).then(() => { this.isUrlFilters = false; }); } getSuspiciousScores(): Observable { this.currentPage = 1; return this.localCacheService.fetchData( 'suspiciousScores', `${environment.apiUrl}/suspicious-scores`, 15 ); } filterScores(): void { // Access the filters from the filterManager const filters = this.filterManager.filters; // Trim and convert to lowercase for case-insensitive comparison const searchUsername = filters.searchUsername?.trim().toLowerCase(); const searchBeatmap = filters.searchBeatmap?.trim().toLowerCase(); this.filteredScores = this.originalScores.filter((score) => { // Apply username filter if specified, case-insensitive const usernameMatch = searchUsername ? score.username.toLowerCase().includes(searchUsername) : true; // Apply beatmap filter if specified, case-insensitive const beatmapMatch = searchBeatmap ? score.beatmap_title.toLowerCase().includes(searchBeatmap) : true; // Apply PP filter if specified const ppMatch = (filters.minPP == null || score.pp >= filters.minPP) && (filters.maxPP == null || score.pp <= filters.maxPP); // Apply UR filter if specified const urMatch = (filters.minUR == null || score.ur >= filters.minUR) && (filters.maxUR == null || score.ur <= filters.maxUR); return usernameMatch && beatmapMatch && ppMatch && urMatch; }); // Presumably persists the current state of filters for future sessions this.filterManager.persistToLocalStorage(); if(this.filterManager.filters.sorting) { let field = this.filterManager.filters.sorting.split("-")[0]; let order = this.filterManager.filters.sorting.split("-")[1]; //@ts-ignore this.sortScores(field, order); } } sortScores(field: 'pp' | 'ur' | 'beatmap_star_rating' | 'date', order: 'asc' | 'desc'): void { if(this.isUrlFilters && this.filterManager.filters.sorting != `${field}-${order}`) { return; } this.filterManager.filters.sorting = `${field}-${order}` this.filterManager.persistToLocalStorage(); this.currentPage = 1; this.filteredScores.sort((a: SuspiciousScore, b: SuspiciousScore): number => { let compareValue = 0; if (field === 'pp' || field === 'ur' || field === 'beatmap_star_rating') { compareValue = a[field] - b[field]; } else if (field === 'date') { compareValue = a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : 0; } return order === 'asc' ? compareValue : -compareValue; }); } }