Add player count display with periodic updates to home page

This commit is contained in:
akastijn 2025-10-18 02:43:23 +02:00
parent 6ad3b5221a
commit 29a28e712e
2 changed files with 177 additions and 143 deletions

View File

@ -1,157 +1,160 @@
<ng-container>
<app-header [current_page]="'home'" height="100vh"
[background_image]="'/public/img/backgrounds/120spawn-min.png'">
[background_image]="'/public/img/backgrounds/120spawn-min.png'">
<div class="title" header-content>
<h1 style="display: none;">Altitude</h1>
<img id="header-img" ngSrc="/public/img/logos/logo.png" alt="The Altitude Minecraft Server" height="319"
width="550">
<h2 style="font-size: 2.5em;" id="homeh2">Altitude now on {{ ALTITUDE_VERSION }}!</h2>
<a id="scroll-button" (click)="scrollToSection()">
<span></span>
<p style="display: none;">Scroll Down</p>
</a>
</div>
</app-header>
<main>
<section id="scrollingpoint" style="background: #202020; text-align: center; padding: 80px 0;">
<!-- TODO load player count from old api or backend?-->
width="550">
<h2 style="font-size: 2.5em;" id="homeh2">Altitude now on {{ ALTITUDE_VERSION }}!</h2>
<a id="scroll-button" (click)="scrollToSection()">
<span></span>
<p style="display: none;">Scroll Down</p>
</a>
</div>
</app-header>
<main>
<section id="scrollingpoint" style="background: #202020; text-align: center; padding: 80px 0;">
@if (this.playerCount() === null) {
<h2 style="color: white;"><span class="player-count">Loading...</span></h2>
<h2 style="color: white;">Server IP: play.alttd.com</h2>
<div style="padding-top: 35px;">
<app-copy-ip></app-copy-ip>
</div>
</section>
<section class="darkmodeSection">
<div class="container">
<div class="paragraph">
<h2>Adventure Begins</h2>
<p>You awake in a strange town, where are you? There are residents running about trading with each other and
stories of distant realms with more towns. It's time to write your story. Welcome to Altitude, the laid-back
} @else {
<h2 style="color: white;"><span class="player-count">{{ playerCount() }}</span></h2>
}
<h2 style="color: white;">Server IP: play.alttd.com</h2>
<div style="padding-top: 35px;">
<app-copy-ip></app-copy-ip>
</div>
</section>
<section class="darkmodeSection">
<div class="container">
<div class="paragraph">
<h2>Adventure Begins</h2>
<p>You awake in a strange town, where are you? There are residents running about trading with each other and
stories of distant realms with more towns. It's time to write your story. Welcome to Altitude, the laid-back
community-oriented server that hosts your home for Minecraft.</p>
</div>
<img ngSrc="/public/img/items/bookquill.png" style="width: 150px; align-self: center; margin: 0 auto;"
alt="Alternative Altitude Server Logo"
height="150" width="150">
</div>
</section>
<!--
<section id="section1">
<div class="container" id="video">
<h2 style="display: none;">YouTube Trailer</h2>
<div style="border-radius:5px;overflow:hidden;position:relative;width:100%">
<img style="width: 100%" src="https://img.youtube.com/vi/Nzbj9Dbv5Wk/maxresdefault.jpg" alt="Altitude YouTube Trailer">
<div id="youtube">
<div class="play" onclick="playVideo()"></div>
</div>
<img ngSrc="/public/img/items/bookquill.png" style="width: 150px; align-self: center; margin: 0 auto;"
alt="Alternative Altitude Server Logo"
height="150" width="150">
</div>
</div>
</section>
-->
<section class="darkmodeSection">
<div class="container" style="padding: 10px 0 80px 0">
<iframe id="discord-widget" src="https://discordapp.com/widget?id=141644560005595136&theme=dark"></iframe>
<div class="paragraph" id="discord-p">
<h2>Meet the Community</h2>
<p>Altitude is home to players young and old from all around the globe, and here, everyone is family. Altitude
is your place to get together with friends and relax - and maybe enjoy some survival too. Altitude is
intended for older players, but all are welcome!</p>
<p>Don't have Discord? Keep up with news and announcements at the <a href="alttd.com/blog">blog</a>.</p>
<div style="display: flex; justify-content: center;">
<a target="_blank" rel="noopener" href="https://discordapp.com/invite/TGqpzCJ">
<div style="margin-top: 30px;" class="button-outer">
<span class="button-inner">Join Our Discord</span>
</div>
</a>
</div>
</section>
<!--
<section id="section1">
<div class="container" id="video">
<h2 style="display: none;">YouTube Trailer</h2>
<div style="border-radius:5px;overflow:hidden;position:relative;width:100%">
<img style="width: 100%" src="https://img.youtube.com/vi/Nzbj9Dbv5Wk/maxresdefault.jpg" alt="Altitude YouTube Trailer">
<div id="youtube">
<div class="play" onclick="playVideo()"></div>
</div>
</div>
</section>
<section id="section2">
<div class="customContainer">
<h2 style="color: white; padding-bottom: 35px; font-size: 2.8em;">Survival Shaped by You</h2>
<p style="color: white; padding-bottom: 35px; font-size: 1.1em; margin: auto;">Altitude is built by the
community, for the community. We've added features requested by our members and several custom plugins to
create our "perfect" survival experience.</p>
<div class="survivalShapedContainerPlugins">
<div class="pluginColumn">
<h2>McMMO & MyPet</h2>
<p>Two of the most requested plugins on Altitude, level up yourself and your pet with these MMO-based
plugins!</p>
<a [routerLink]="['/mypet']">
<div class="button-outer">
<span class="button-inner">MyPet Info</span>
</div>
</div>
</a>
</div>
</section>
-->
<section class="darkmodeSection">
<div class="container" style="padding: 10px 0 80px 0">
<iframe id="discord-widget" src="https://discordapp.com/widget?id=141644560005595136&theme=dark"></iframe>
<div class="paragraph" id="discord-p">
<h2>Meet the Community</h2>
<p>Altitude is home to players young and old from all around the globe, and here, everyone is family. Altitude
is your place to get together with friends and relax - and maybe enjoy some survival too. Altitude is
intended for older players, but all are welcome!</p>
<p>Don't have Discord? Keep up with news and announcements at the <a href="alttd.com/blog">blog</a>.</p>
<div style="display: flex; justify-content: center;">
<a target="_blank" rel="noopener" href="https://discordapp.com/invite/TGqpzCJ">
<div style="margin-top: 30px;" class="button-outer">
<span class="button-inner">Join Our Discord</span>
</div>
</a>
<div class="pluginColumn">
<h2>Dynamic map</h2>
<p>See the world and the players around it in real-time! The map shows the entire survival world with claims
and warps.</p>
<a [routerLink]="['/map']">
<div class="button-outer">
<span class="button-inner">Visit Map</span>
</div>
</div>
</a>
</div>
</section>
<section id="section2">
<div class="customContainer">
<h2 style="color: white; padding-bottom: 35px; font-size: 2.8em;">Survival Shaped by You</h2>
<p style="color: white; padding-bottom: 35px; font-size: 1.1em; margin: auto;">Altitude is built by the
community, for the community. We've added features requested by our members and several custom plugins to
create our "perfect" survival experience.</p>
<div class="survivalShapedContainerPlugins">
<div class="pluginColumn">
<h2>McMMO & MyPet</h2>
<p>Two of the most requested plugins on Altitude, level up yourself and your pet with these MMO-based
plugins!</p>
<a [routerLink]="['/mypet']">
<div class="button-outer">
<span class="button-inner">MyPet Info</span>
</div>
</a>
<div class="pluginColumn">
<h2>Player Shops</h2>
<p>Our economy is built on player shops, encouraging player interaction and putting the control in your
hands.</p>
<a [routerLink]="['/economy']">
<div class="button-outer">
<span class="button-inner">Shop Guide</span>
</div>
<div class="pluginColumn">
<h2>Dynamic map</h2>
<p>See the world and the players around it in real-time! The map shows the entire survival world with claims
and warps.</p>
<a [routerLink]="['/map']">
<div class="button-outer">
<span class="button-inner">Visit Map</span>
</div>
</a>
</div>
<div class="pluginColumn">
<h2>Player Shops</h2>
<p>Our economy is built on player shops, encouraging player interaction and putting the control in your
hands.</p>
<a [routerLink]="['/economy']">
<div class="button-outer">
<span class="button-inner">Shop Guide</span>
</div>
</a>
</div>
</div>
</a>
</div>
</section>
<section class="remove-mobile darkmodeSection">
<div class="customContainer">
<h2 style="padding-bottom: 35px; font-size: 2.8em;">Community Builds</h2>
<p style="padding-bottom: 35px; font-size: 1.1em; margin: auto;">Thank you to our brilliant players for sharing
their impressive builds. Take a look at some of them here!<br>
If you know of any builds that should be featured, please let us know!</p>
<div class="sliderWrapper">
<div class="sliderContent">
<div class="indexSlider">
<div id="go-left" (click)="previousSlide()" class="circleBehind goLeft"></div>
<div id="go-right" (click)="nextSlide()" class="circleBehind goRight"></div>
<div class="display">
</div>
</div>
</section>
<section class="remove-mobile darkmodeSection">
<div class="customContainer">
<h2 style="padding-bottom: 35px; font-size: 2.8em;">Community Builds</h2>
<p style="padding-bottom: 35px; font-size: 1.1em; margin: auto;">Thank you to our brilliant players for sharing
their impressive builds. Take a look at some of them here!<br>
If you know of any builds that should be featured, please let us know!</p>
<div class="sliderWrapper">
<div class="sliderContent">
<div class="indexSlider">
<div id="go-left" (click)="previousSlide()" class="circleBehind goLeft"></div>
<div id="go-right" (click)="nextSlide()" class="circleBehind goRight"></div>
<div class="display">
<span class="slide"
[style.background-image]="'url(' + slide + ')'"
[style.opacity]="carouselOpacity"
[style.display]="'block'"></span>
</div>
</div>
<div class="dots">
@for (slide of getSlideIndices(); track slide) {
<span class="dot" (click)="setSlide(slide)" [ngClass]="getDotClass(slide)"></span>
}
</div>
[style.background-image]="'url(' + slide + ')'"
[style.opacity]="carouselOpacity"
[style.display]="'block'"></span>
</div>
</div>
</div>
</section>
<section style="background: #202020;">
<div class="customContainer">
<h2 id="quote" style="color: white; font-family: 'minecraft-text',sans-serif; line-height: 1.3em;">"Great
community, great people, great server all round . . . If it can bring back my love for minecraft, it could
work wonders for you."</h2>
<p style="color: white; margin-top:30px;">- /u/seanhanley1993</p>
</div>
</section>
<section class="darkmodeSection">
<div class="customContainer">
<img ngSrc="/public/img/logos/log.png" alt="Alternative Altitude Server Logo" height="129"
width="120">
<h2 style="margin: 20px 0; font-size: 1.8em; padding-bottom: 0 !important;">play.alttd.com</h2>
<app-copy-ip></app-copy-ip>
<div class="dots">
@for (slide of getSlideIndices(); track slide) {
<span class="dot" (click)="setSlide(slide)" [ngClass]="getDotClass(slide)"></span>
}
</div>
</section>
<a (click)="this.scrollService.scrollToTop()" class="scroll-up-button, active">
<span></span>
<p style="display: none;">Scroll Down</p>
</a>
</main>
</ng-container>
</div>
</div>
</div>
</section>
<section style="background: #202020;">
<div class="customContainer">
<h2 id="quote" style="color: white; font-family: 'minecraft-text',sans-serif; line-height: 1.3em;">"Great
community, great people, great server all round . . . If it can bring back my love for minecraft, it could
work wonders for you."</h2>
<p style="color: white; margin-top:30px;">- /u/seanhanley1993</p>
</div>
</section>
<section class="darkmodeSection">
<div class="customContainer">
<img ngSrc="/public/img/logos/log.png" alt="Alternative Altitude Server Logo" height="129"
width="120">
<h2 style="margin: 20px 0; font-size: 1.8em; padding-bottom: 0 !important;">play.alttd.com</h2>
<app-copy-ip></app-copy-ip>
</div>
</section>
<a (click)="this.scrollService.scrollToTop()" class="scroll-up-button, active">
<span></span>
<p style="display: none;">Scroll Down</p>
</a>
</main>
</ng-container>

View File

@ -1,12 +1,22 @@
import {Component, OnInit} from '@angular/core';
import {Component, inject, OnDestroy, OnInit, signal} from '@angular/core';
import {Title} from '@angular/platform-browser';
import {ALTITUDE_VERSION} from '@custom-types/constant';
import {ScrollService} from '@services/scroll.service';
import { CommonModule, NgOptimizedImage } from '@angular/common';
import {CommonModule, NgOptimizedImage} from '@angular/common';
import {HeaderComponent} from '@header/header.component';
import {CopyIpComponent} from '@shared-components/copy-ip/copy-ip.component';
import {RouterLink} from '@angular/router';
import {JwtHelperService} from '@auth0/angular-jwt';
import {interval, map} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {startWith} from 'rxjs/operators';
interface MinecraftServerStatus {
online?: boolean;
players?: {
online?: number;
};
}
@Component({
standalone: true,
@ -24,9 +34,16 @@ import {JwtHelperService} from '@auth0/angular-jwt';
templateUrl: './home.component.html',
styleUrl: './home.component.scss'
})
export class HomeComponent implements OnInit {
constructor(private titleService: Title, public scrollService: ScrollService) {
}
export class HomeComponent implements OnInit, OnDestroy {
private httpClient = inject(HttpClient);
private titleService = inject(Title);
public scrollService = inject(ScrollService);
public playerCount = signal<null | number>(null);
private playerCountUpdateInterval = interval(60000).pipe(
startWith(() => 0),
).subscribe(() => {
this.updatePlayerCount();
});
private slides: string[] = ["/public/img/backgrounds/caruselimage2.png",
"/public/img/backgrounds/caruselimage4.png",
@ -44,6 +61,10 @@ export class HomeComponent implements OnInit {
this.randomizeSlides()
}
ngOnDestroy(): void {
this.playerCountUpdateInterval.unsubscribe();
}
private randomizeSlides(): void {
const array = [...this.slides];
@ -108,5 +129,15 @@ export class HomeComponent implements OnInit {
}
}
private updatePlayerCount() {
this.httpClient.get<MinecraftServerStatus>('https://api.mcsrvstat.us/2/play.alttd.com').pipe(
map(response => {
if (response.online && response.players && response.players.online) {
this.playerCount.set(response.players.online);
}
})
).subscribe();
}
protected readonly ALTITUDE_VERSION = ALTITUDE_VERSION;
}