Minetrack/assets/js/app.js
2023-12-30 23:03:54 +00:00

168 lines
5.7 KiB
JavaScript

import { FavoritesManager } from "./favorites";
import { GraphDisplayManager } from "./graph";
import { PercentageBar } from "./percbar";
import { ServerRegistry } from "./servers";
import { SocketManager } from "./socket";
import { SortController } from "./sort";
import { Caption, Tooltip, formatNumber } from "./util";
export class App {
publicConfig;
constructor() {
this.tooltip = new Tooltip();
this.caption = new Caption();
this.serverRegistry = new ServerRegistry(this);
this.socketManager = new SocketManager(this);
this.sortController = new SortController(this);
this.graphDisplayManager = new GraphDisplayManager(this);
this.percentageBar = new PercentageBar(this);
this.favoritesManager = new FavoritesManager(this);
this._taskIds = [];
}
// Called once the DOM is ready and the app can begin setup
init() {
this.socketManager.createWebSocket();
}
setPageReady(isReady) {
document.getElementById("push").style.display = isReady ? "block" : "none";
document.getElementById("footer").style.display = isReady
? "block"
: "none";
document.getElementById("status-overlay").style.display = isReady
? "none"
: "block";
}
setPublicConfig(publicConfig) {
this.publicConfig = publicConfig;
this.serverRegistry.assignServers(publicConfig.servers);
// Start repeating frontend tasks once it has received enough data to be considered active
// This simplifies management logic at the cost of each task needing to safely handle empty data
this.initTasks();
}
handleSyncComplete() {
this.caption.hide();
// Load favorites since all servers are registered
this.favoritesManager.loadLocalStorage();
// Run a single bulk server sort instead of per-add event since there may be multiple
this.sortController.show();
this.percentageBar.redraw();
// The data may not be there to correctly compute values, but run an attempt
// Otherwise they will be updated by #initTasks
this.updateGlobalStats();
}
initTasks() {
this._taskIds.push(setInterval(this.sortController.sortServers, 5000));
}
handleDisconnect() {
this.tooltip.hide();
// Reset individual tracker elements to flush any held data
this.serverRegistry.reset();
this.socketManager.reset();
this.sortController.reset();
this.graphDisplayManager.reset();
this.percentageBar.reset();
// Undefine publicConfig, resynced during the connection handshake
this.publicConfig = undefined;
// Clear all task ids, if any
this._taskIds.forEach(clearInterval);
this._taskIds = [];
// Reset hidden values created by #updateGlobalStats
this._lastTotalPlayerCount = undefined;
this._lastServerRegistrationCount = undefined;
// Reset modified DOM structures
document.getElementById("stat_totalPlayers").innerText = 0;
document.getElementById("stat_networks").innerText = 0;
this.setPageReady(false);
}
getTotalPlayerCount() {
return this.serverRegistry
.getServerRegistrations()
.map((serverRegistration) => serverRegistration.playerCount)
.reduce((sum, current) => sum + current, 0);
}
addServer = (serverId, payload, timestampPoints) => {
// Even if the backend has never pinged the server, the frontend is promised a placeholder object.
// result = undefined
// error = defined with "Waiting" description
// info = safely defined with configured data
const serverRegistration =
this.serverRegistry.createServerRegistration(serverId);
serverRegistration.initServerStatus(payload);
// playerCountHistory is only defined when the backend has previous ping data
// undefined playerCountHistory means this is a placeholder ping generated by the backend
if (typeof payload.playerCountHistory !== "undefined") {
// Push the historical data into the graph
// This will trim and format the data so it is ready for the graph to render once init
serverRegistration.addGraphPoints(
payload.playerCountHistory,
timestampPoints
);
// Set initial playerCount to the payload's value
// This will always exist since it is explicitly generated by the backend
// This is used for any post-add rendering of things like the percentageBar
serverRegistration.playerCount = payload.playerCount;
}
// Create the plot instance internally with the restructured and cleaned data
serverRegistration.buildPlotInstance();
// Handle the last known state (if any) as an incoming update
// This triggers the main update pipeline and enables centralized update handling
serverRegistration.updateServerStatus(
payload,
this.publicConfig.minecraftVersions
);
// Allow the ServerRegistration to bind any DOM events with app instance context
serverRegistration.initEventListeners();
};
updateGlobalStats = () => {
// Only redraw when needed
// These operations are relatively cheap, but the site already does too much rendering
const totalPlayerCount = this.getTotalPlayerCount();
if (totalPlayerCount !== this._lastTotalPlayerCount) {
this._lastTotalPlayerCount = totalPlayerCount;
document.getElementById("stat_totalPlayers").innerText =
formatNumber(totalPlayerCount);
}
// Only redraw when needed
// These operations are relatively cheap, but the site already does too much rendering
const serverRegistrationCount =
this.serverRegistry.getServerRegistrations().length;
if (serverRegistrationCount !== this._lastServerRegistrationCount) {
this._lastServerRegistrationCount = serverRegistrationCount;
document.getElementById("stat_networks").innerText =
serverRegistrationCount;
}
};
}