Profile page, tweaked banlist/follows
This commit is contained in:
parent
6a9b14e7e9
commit
482079fb8f
@ -40,6 +40,7 @@ class FollowsController(
|
||||
|
||||
@GetMapping("follows")
|
||||
fun getFollowsBanStatus(): ResponseEntity<FollowsBanStatusResponse> {
|
||||
authService.getCurrentUser().userId
|
||||
val follows = dslContext.select(
|
||||
USERS.USER_ID,
|
||||
USERS.USERNAME,
|
||||
|
||||
@ -6,6 +6,7 @@ import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@Service
|
||||
class AuthService(
|
||||
@ -14,7 +15,11 @@ class AuthService(
|
||||
|
||||
data class UserInfo(
|
||||
val userId: Long,
|
||||
val username: String
|
||||
val username: String,
|
||||
|
||||
val country: String?,
|
||||
val joinDate: OffsetDateTime?,
|
||||
|
||||
)
|
||||
|
||||
fun isAdmin(): Boolean {
|
||||
@ -39,7 +44,9 @@ class AuthService(
|
||||
|
||||
return UserInfo(
|
||||
userId = (userDetails.attributes["id"] as Int).toLong(),
|
||||
username = userDetails.attributes["username"] as String
|
||||
username = userDetails.attributes["username"] as String,
|
||||
country = userDetails.attributes["countryCode"] as String?,
|
||||
joinDate = OffsetDateTime.parse(userDetails.attributes["joinDate"] as String)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ RUN sed -i '238s|return \[x for x in arr if lower_limit < x < upper_limit\]|arr_
|
||||
|
||||
COPY ./src/ ./src/
|
||||
|
||||
ENV GUNICORN_CMD_ARGS="--bind=0.0.0.0:5000 --workers=10"
|
||||
ENV GUNICORN_CMD_ARGS="--bind=0.0.0.0:5000 --workers=16"
|
||||
|
||||
# Run gunicorn with the application
|
||||
CMD ["gunicorn", "--chdir", "src", "main:app"]
|
||||
@ -9,7 +9,7 @@ import {ViewReplayPairComponent} from "./view-replay-pair/view-replay-pair.compo
|
||||
import {SearchComponent} from "./search/search.component";
|
||||
import {ContributeComponent} from "./contribute/contribute.component";
|
||||
import {BanlistComponent} from "./banlist/banlist.component";
|
||||
import {FollowsComponent} from "./follows/follows.component";
|
||||
import {ProfileComponent} from "./profile/profile.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: 'sus/:f', component: ViewSuspiciousScoresComponent, title: '/sus/'},
|
||||
@ -25,7 +25,7 @@ const routes: Routes = [
|
||||
|
||||
{path: 'p/:replay1Id/:replay2Id', component: ViewReplayPairComponent},
|
||||
|
||||
{path: 'follows', component: FollowsComponent, title: '/follows/'},
|
||||
{path: 'profile', component: ProfileComponent},
|
||||
{path: 'banlist', component: BanlistComponent, title: '/ban/'},
|
||||
{path: 'contribute', component: ContributeComponent, title: '/contribute/ <3'},
|
||||
|
||||
|
||||
@ -15,3 +15,10 @@
|
||||
.link-pink:hover {
|
||||
color: rgba(234, 78, 179, 0.97)
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
padding: 1px 2px 2px 1px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border: 1px dashed rgba(151, 151, 151, 0.97);
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</form>
|
||||
<div style="margin-top: 3px">
|
||||
<ng-container *ngIf="this.userService.isUserLoggedIn()">
|
||||
hi, <span class="user-details" [title]="this.userService.currentUser?.username">{{this.userService.currentUser?.username}}</span> <a [href]="this.userService.getLogoutUrl()">Logout</a>
|
||||
hi, <span class="user-details" [title]="this.userService.currentUser?.username"><a [routerLink]="['/profile']"> {{this.userService.currentUser?.username}}</a></span> <a class="logout-btn" [href]="this.userService.getLogoutUrl()">Logout</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!this.userService.isUserLoggedIn()">
|
||||
<a [href]="this.userService.getLoginUrl()">Login</a>
|
||||
@ -26,6 +26,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main term text-center statistics">
|
||||
Players: {{ this.statistics?.total_users | number }} | Beatmaps: {{ this.statistics?.total_beatmaps | number }} | Scores: {{ this.statistics?.total_scores | number }}
|
||||
<ng-container *ngIf="this.statistics?.total_bans">
|
||||
| <a [routerLink]="['/banlist']">Bans: {{ this.statistics?.total_bans | number }}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
<div class="text-center version">
|
||||
v20240309
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Router, RouterLink, RouterOutlet} from "@angular/router";
|
||||
import {UserService} from "../corelib/service/user.service";
|
||||
import {NgIf} from '@angular/common';
|
||||
import {DecimalPipe, NgIf} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Observable} from "rxjs";
|
||||
import {environment} from "../environments/environment";
|
||||
import {LocalCacheService} from "../corelib/service/local-cache.service";
|
||||
|
||||
interface Statistics {
|
||||
total_beatmaps: number;
|
||||
total_users: number;
|
||||
total_scores: number;
|
||||
total_replay_scores: number;
|
||||
total_replay_similarity: number;
|
||||
total_bans: number;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -11,18 +24,34 @@ import {FormsModule} from '@angular/forms';
|
||||
RouterLink,
|
||||
FormsModule,
|
||||
NgIf,
|
||||
RouterOutlet
|
||||
RouterOutlet,
|
||||
DecimalPipe
|
||||
],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
statistics: Statistics | null = null;
|
||||
term: string = '';
|
||||
|
||||
constructor(private router: Router,
|
||||
private localCacheService: LocalCacheService,
|
||||
public userService: UserService
|
||||
) {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getStatistics().subscribe((response: Statistics) => {
|
||||
this.statistics = response;
|
||||
});
|
||||
}
|
||||
|
||||
getStatistics(): Observable<Statistics> {
|
||||
return this.localCacheService.fetchData<Statistics>(
|
||||
'statistics',
|
||||
`${environment.apiUrl}/stats`,
|
||||
60
|
||||
);
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
|
||||
@ -21,7 +21,16 @@
|
||||
<ng-template #nullTemplate>
|
||||
<code>null</code>
|
||||
</ng-template>
|
||||
<table *ngIf="this.banlist">
|
||||
<ng-container *ngIf="this.banlist">
|
||||
<fieldset class="mb-2">
|
||||
<legend>tools</legend>
|
||||
<div class="text-center">
|
||||
<button (click)="this.downloadFilesService.downloadCSV(this.banlist.users, ['userId', 'username', 'secondsPlayed', 'pp', 'rank', 'isBanned', 'approximateBanTime', 'lastUpdate'], 'nise-banlist')">Download .csv</button>
|
||||
<button (click)="this.downloadFilesService.downloadJSON(this.banlist.users, 'nise-banlist')">Download .json</button>
|
||||
<button (click)="this.downloadFilesService.downloadXLSX(this.banlist.users, 'nise-banlist')">Download .xlsx</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Username</th>
|
||||
@ -70,6 +79,25 @@
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center mt-2">
|
||||
<p>Total results: {{ this.banlist.pagination.totalResults | number }}</p>
|
||||
<p>Page: {{ this.banlist.pagination.currentPage | number }} / {{ this.banlist.pagination.totalPages | number }}</p>
|
||||
<div class="mb-2">
|
||||
<button *ngIf="this.banlist.pagination.currentPage > 5" (click)="this.getBanlist(1)" style="margin-right: 5px">1</button>
|
||||
<span *ngIf="this.banlist.pagination.currentPage > 6">... </span>
|
||||
<button *ngFor="let page of [].constructor(Math.min(this.banlist.pagination.totalPages, 10)) | calculatePageRange:this.banlist.pagination.currentPage:this.banlist.pagination.totalPages; let i = index"
|
||||
(click)="this.getBanlist(page)"
|
||||
[disabled]="page == this.banlist.pagination.currentPage"
|
||||
style="margin-right: 5px">
|
||||
{{ page }}
|
||||
</button>
|
||||
<span *ngIf="this.banlist.pagination.currentPage < this.banlist.pagination.totalPages - 5">... </span>
|
||||
<button *ngIf="this.banlist.pagination.currentPage < this.banlist.pagination.totalPages - 4" (click)="this.getBanlist(this.banlist.pagination.totalPages)" style="margin-right: 5px">{{ this.banlist.pagination.totalPages }}</button>
|
||||
</div>
|
||||
<button (click)="this.getBanlist(this.banlist.pagination.currentPage - 1)" [disabled]="this.banlist.pagination.currentPage == 1">← Previous</button>
|
||||
<button (click)="this.getBanlist(this.banlist.pagination.currentPage + 1)" [disabled]="this.banlist.pagination.currentPage == this.banlist.pagination.totalPages" style="margin-left: 5px">Next →</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6,6 +6,8 @@ import {calculateTimeAgo, formatDuration} from "../format";
|
||||
import {RouterLink} from "@angular/router";
|
||||
import {CuteProgressbarComponent} from "../../corelib/components/cute-progressbar/cute-progressbar.component";
|
||||
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
||||
import {DownloadFilesService} from "../../corelib/service/download-files.service";
|
||||
import {CalculatePageRangePipe} from "../../corelib/calculate-page-range.pipe";
|
||||
|
||||
interface BanlistResponse {
|
||||
pagination: BanlistPagination;
|
||||
@ -41,7 +43,8 @@ interface BanlistEntry {
|
||||
DatePipe,
|
||||
DecimalPipe,
|
||||
CuteProgressbarComponent,
|
||||
CuteLoadingComponent
|
||||
CuteLoadingComponent,
|
||||
CalculatePageRangePipe
|
||||
],
|
||||
templateUrl: './banlist.component.html',
|
||||
styleUrl: './banlist.component.css'
|
||||
@ -51,15 +54,18 @@ export class BanlistComponent implements OnInit {
|
||||
isLoading = true;
|
||||
banlist: BanlistResponse | null = null;
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
public downloadFilesService: DownloadFilesService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getBanlist();
|
||||
}
|
||||
|
||||
getBanlist(): void {
|
||||
getBanlist(page: number = 1): void {
|
||||
const body = {
|
||||
page: 1
|
||||
page: page
|
||||
}
|
||||
this.httpClient.post<BanlistResponse>(`${environment.apiUrl}/banlist`, body).subscribe(response => {
|
||||
this.banlist = response;
|
||||
@ -69,4 +75,5 @@ export class BanlistComponent implements OnInit {
|
||||
|
||||
protected readonly calculateTimeAgo = calculateTimeAgo;
|
||||
protected readonly formatDuration = formatDuration;
|
||||
protected readonly Math = Math;
|
||||
}
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
<div class="main container">
|
||||
|
||||
<div class="subcontainer" style="width: 100%">
|
||||
<div class="term" style="width: 100%">
|
||||
<div class="main term" style="width: 100%">
|
||||
|
||||
<div style="overflow: hidden;">
|
||||
<img src="assets/contribute.png" width="184" style="float: left; margin-right: 20px;">
|
||||
@ -12,7 +9,7 @@
|
||||
<div class="text-center mt-2 mb-2">
|
||||
<a href="https://patreon.com/nise_moe" target="_blank" class="btn btn-patreon">Become a Patreon</a>
|
||||
</div>
|
||||
<p>the money will be used to pay for the server hosting and development time.</p>
|
||||
<p>the money will be used to pay for the server hosting and development costs.</p>
|
||||
<table style="display: block;">
|
||||
<tbody>
|
||||
<tr>
|
||||
@ -32,8 +29,4 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
<div class="main term text-center statistics">
|
||||
Players: {{ this.statistics?.total_users | number }} | Beatmaps: {{ this.statistics?.total_beatmaps | number }} | Scores: {{ this.statistics?.total_scores | number }}
|
||||
<ng-container *ngIf="this.statistics?.total_bans">
|
||||
| <a [routerLink]="['/banlist']">Bans: {{ this.statistics?.total_bans | number }}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="main container">
|
||||
<div class="main container" style="width: 886px !important;">
|
||||
|
||||
<div class="subcontainer">
|
||||
<div class="term">
|
||||
|
||||
@ -1,24 +1,14 @@
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Observable, Subscription} from "rxjs";
|
||||
import {Subscription} from "rxjs";
|
||||
import {environment} from "../../environments/environment";
|
||||
import {LocalCacheService} from "../../corelib/service/local-cache.service";
|
||||
import {RxStompService} from "../../corelib/stomp/stomp.service";
|
||||
import {Message} from "@stomp/stompjs/esm6";
|
||||
import {ErrorDistribution, ReplayData, ReplayDataChart, ReplayDataSimilarScore} from "../replays";
|
||||
import {ReplayData} from "../replays";
|
||||
import {DecimalPipe, NgForOf, NgIf} from "@angular/common";
|
||||
import {Router, RouterLink} from "@angular/router";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
||||
|
||||
interface Statistics {
|
||||
total_beatmaps: number;
|
||||
total_users: number;
|
||||
total_scores: number;
|
||||
total_replay_scores: number;
|
||||
total_replay_similarity: number;
|
||||
total_bans: number;
|
||||
}
|
||||
|
||||
interface AnalyzeReplayResponse {
|
||||
id: string;
|
||||
}
|
||||
@ -41,13 +31,11 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
liveScores: ReplayData[] = [];
|
||||
|
||||
liveScoresSub: Subscription | undefined;
|
||||
statistics: Statistics | null = null;
|
||||
wantsConnection: boolean = true;
|
||||
|
||||
loading = false;
|
||||
|
||||
constructor(
|
||||
private localCacheService: LocalCacheService,
|
||||
private router: Router,
|
||||
private httpClient: HttpClient,
|
||||
private rxStompService: RxStompService,
|
||||
@ -63,10 +51,6 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.subscribe();
|
||||
}
|
||||
|
||||
this.getStatistics().subscribe((response: Statistics) => {
|
||||
this.statistics = response;
|
||||
});
|
||||
}
|
||||
|
||||
private subscribe() {
|
||||
@ -96,14 +80,6 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
getStatistics(): Observable<Statistics> {
|
||||
return this.localCacheService.fetchData<Statistics>(
|
||||
'statistics',
|
||||
`${environment.apiUrl}/stats`,
|
||||
60
|
||||
);
|
||||
}
|
||||
|
||||
uploadReplay(event: any) {
|
||||
if (event.target.files.length <= 0) {
|
||||
return;
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
<div class="main term mb-2">
|
||||
<div class="fade-stuff">
|
||||
<h1 class="mb-4"><span class="text-muted">hi,</span> {{ this.userService.currentUser?.username }}</h1>
|
||||
|
||||
<h1 class="mb-4"># follow-list</h1>
|
||||
<table *ngIf="this.follows">
|
||||
<ng-template #noFollows>
|
||||
<div class="text-center">
|
||||
<p>You are not following anyone!</p>
|
||||
<p>You can follow users by going on their profile and clicking the <code>(+) Add follow</code> buttan.</p>
|
||||
<p>Then, they'll appear here, and you'll be able to check if they've been banned or not.</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
<table *ngIf="this.follows && this.follows.follows.length > 0; else noFollows">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Username</th>
|
||||
@ -4,6 +4,8 @@ import {environment} from "../../environments/environment";
|
||||
import {JsonPipe, NgForOf, NgIf} from "@angular/common";
|
||||
import {calculateTimeAgo} from "../format";
|
||||
import {RouterLink} from "@angular/router";
|
||||
import {UserService} from "../../corelib/service/user.service";
|
||||
import {Title} from "@angular/platform-browser";
|
||||
|
||||
interface FollowsBanStatusResponse {
|
||||
follows: FollowsBanStatusEntry[];
|
||||
@ -18,7 +20,7 @@ interface FollowsBanStatusEntry {
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-follows',
|
||||
selector: 'app-profile',
|
||||
standalone: true,
|
||||
imports: [
|
||||
JsonPipe,
|
||||
@ -26,16 +28,21 @@ interface FollowsBanStatusEntry {
|
||||
NgIf,
|
||||
RouterLink
|
||||
],
|
||||
templateUrl: './follows.component.html',
|
||||
styleUrl: './follows.component.css'
|
||||
templateUrl: './profile.component.html',
|
||||
styleUrl: './profile.component.css'
|
||||
})
|
||||
export class FollowsComponent implements OnInit {
|
||||
export class ProfileComponent implements OnInit {
|
||||
|
||||
follows: FollowsBanStatusResponse | null = null;
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private title: Title,
|
||||
public userService: UserService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.title.setTitle(`hi, ${this.userService.currentUser!.username}`);
|
||||
this.getFollows();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user