194 lines
5.6 KiB
TypeScript
194 lines
5.6 KiB
TypeScript
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<SuspiciousScoresFilter>("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<SuspiciousScore[]> {
|
|
this.currentPage = 1;
|
|
return this.localCacheService.fetchData<SuspiciousScore[]>(
|
|
'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;
|
|
});
|
|
}
|
|
|
|
|
|
|
|
}
|