| class GameUI {
|
| constructor() {
|
|
|
| this.state = {
|
| currentPage: 'landing',
|
| isAnimating: false,
|
| darkMode: true,
|
| animationDuration: 300
|
| };
|
|
|
|
|
| this.elements = {
|
| pages: {},
|
| buttons: {},
|
| containers: {},
|
| overlays: {}
|
| };
|
|
|
|
|
| this.initialize();
|
| }
|
|
|
| async initialize() {
|
|
|
| if (document.readyState === 'loading') {
|
| document.addEventListener('DOMContentLoaded', () => this.setupUI());
|
| } else {
|
| this.setupUI();
|
| }
|
| }
|
|
|
| setupUI() {
|
|
|
| this.cacheElements();
|
|
|
| this.setupEventListeners();
|
|
|
| this.showPage('landing');
|
| }
|
|
|
| cacheElements() {
|
|
|
| ['landing', 'setup', 'game', 'recording', 'listening', 'voting', 'results'].forEach(pageId => {
|
| this.elements.pages[pageId] = document.getElementById(`${pageId}-page`);
|
| });
|
|
|
|
|
| this.elements.buttons = {
|
| play: document.getElementById('play-button'),
|
| addPlayer: document.getElementById('add-player-button'),
|
| start: document.getElementById('start-button'),
|
| record: document.getElementById('record-button'),
|
| settings: document.getElementById('settings-button')
|
| };
|
|
|
|
|
| this.elements.containers = {
|
| playerList: document.getElementById('player-list'),
|
| questionDisplay: document.getElementById('question-display'),
|
| timerDisplay: document.getElementById('timer-display'),
|
| recordingVisualizer: document.getElementById('recording-visualizer'),
|
| votingOptions: document.getElementById('voting-options')
|
| };
|
|
|
|
|
| this.elements.overlays = {
|
| loading: document.getElementById('loading-overlay'),
|
| error: document.getElementById('error-overlay'),
|
| settings: document.getElementById('settings-overlay')
|
| };
|
| }
|
|
|
| setupEventListeners() {
|
|
|
| Object.entries(this.elements.buttons).forEach(([key, button]) => {
|
| if (button) {
|
| button.addEventListener('click', () => this.handleButtonClick(key));
|
| }
|
| });
|
|
|
|
|
| document.addEventListener('keydown', (e) => this.handleKeyPress(e));
|
|
|
|
|
| if (this.elements.buttons.settings) {
|
| this.elements.buttons.settings.addEventListener('click', () => this.toggleSettings());
|
| }
|
| }
|
|
|
| handleButtonClick(buttonType) {
|
| switch (buttonType) {
|
| case 'play':
|
| this.transitionToPage('setup');
|
| break;
|
| case 'addPlayer':
|
| window.game.addPlayer();
|
| break;
|
| case 'start':
|
| window.game.startGame();
|
| break;
|
| case 'record':
|
| this.toggleRecording();
|
| break;
|
| default:
|
| console.warn(`Unhandled button type: ${buttonType}`);
|
| }
|
| }
|
|
|
| async transitionToPage(pageName) {
|
| if (this.state.isAnimating || this.state.currentPage === pageName) return;
|
|
|
| this.state.isAnimating = true;
|
|
|
|
|
| const currentPage = this.elements.pages[this.state.currentPage];
|
| if (currentPage) {
|
| await this.animateElement(currentPage, 'fadeOut');
|
| currentPage.classList.remove('active');
|
| }
|
|
|
|
|
| this.state.currentPage = pageName;
|
| const newPage = this.elements.pages[pageName];
|
| if (newPage) {
|
| newPage.classList.add('active');
|
| await this.animateElement(newPage, 'fadeIn');
|
| }
|
|
|
| this.state.isAnimating = false;
|
| }
|
|
|
| async animateElement(element, animation) {
|
| return new Promise(resolve => {
|
| element.classList.add(animation);
|
| setTimeout(() => {
|
| element.classList.remove(animation);
|
| resolve();
|
| }, this.state.animationDuration);
|
| });
|
| }
|
|
|
| updatePlayerList(players) {
|
| if (!this.elements.containers.playerList) return;
|
|
|
| const playerList = this.elements.containers.playerList;
|
| playerList.innerHTML = '';
|
|
|
| players.forEach(player => {
|
| const playerElement = document.createElement('div');
|
| playerElement.className = 'player-avatar';
|
|
|
|
|
| const avatarCircle = document.createElement('div');
|
| avatarCircle.className = 'avatar-circle';
|
| avatarCircle.textContent = player.id;
|
|
|
|
|
| const playerName = document.createElement('div');
|
| playerName.className = 'player-name';
|
| playerName.textContent = player.name;
|
|
|
| playerElement.appendChild(avatarCircle);
|
| playerElement.appendChild(playerName);
|
| playerList.appendChild(playerElement);
|
| });
|
| }
|
|
|
| updateTimer(timeLeft) {
|
| if (!this.elements.containers.timerDisplay) return;
|
|
|
| const minutes = Math.floor(timeLeft / 60);
|
| const seconds = timeLeft % 60;
|
| this.elements.containers.timerDisplay.textContent =
|
| `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
|
|
|
| if (timeLeft <= 10) {
|
| this.elements.containers.timerDisplay.classList.add('warning');
|
| }
|
| }
|
|
|
| showQuestion(question) {
|
| if (!this.elements.containers.questionDisplay) return;
|
|
|
| const questionElement = this.elements.containers.questionDisplay;
|
| questionElement.textContent = question;
|
|
|
|
|
| this.animateElement(questionElement, 'slideIn');
|
| }
|
|
|
| toggleRecording(isRecording) {
|
| if (!this.elements.buttons.record) return;
|
|
|
| const recordButton = this.elements.buttons.record;
|
| recordButton.classList.toggle('recording', isRecording);
|
| recordButton.textContent = isRecording ? 'Stop Recording' : 'Start Recording';
|
| }
|
|
|
| showLoadingOverlay(show, message = 'Loading...') {
|
| if (!this.elements.overlays.loading) return;
|
|
|
| const overlay = this.elements.overlays.loading;
|
| if (show) {
|
| overlay.querySelector('.loading-message').textContent = message;
|
| overlay.classList.add('active');
|
| } else {
|
| overlay.classList.remove('active');
|
| }
|
| }
|
|
|
| showError(message, duration = 3000) {
|
| if (!this.elements.overlays.error) return;
|
|
|
| const errorOverlay = this.elements.overlays.error;
|
| const errorMessage = errorOverlay.querySelector('.error-message');
|
|
|
| errorMessage.textContent = message;
|
| errorOverlay.classList.add('active');
|
|
|
| setTimeout(() => {
|
| errorOverlay.classList.remove('active');
|
| }, duration);
|
| }
|
|
|
| updateVotingOptions(players, currentPlayer) {
|
| if (!this.elements.containers.votingOptions) return;
|
|
|
| const votingContainer = this.elements.containers.votingOptions;
|
| votingContainer.innerHTML = '';
|
|
|
| players.forEach(player => {
|
| if (player.id !== currentPlayer) {
|
| const voteButton = document.createElement('button');
|
| voteButton.className = 'vote-button';
|
| voteButton.dataset.playerId = player.id;
|
|
|
| const playerCircle = document.createElement('div');
|
| playerCircle.className = 'player-circle';
|
| playerCircle.textContent = player.id;
|
|
|
| voteButton.appendChild(playerCircle);
|
| votingContainer.appendChild(voteButton);
|
|
|
| voteButton.addEventListener('click', () => {
|
| window.game.submitVote(player.id);
|
| });
|
| }
|
| });
|
| }
|
|
|
| showResults(results) {
|
| const resultsPage = this.elements.pages.results;
|
| if (!resultsPage) return;
|
|
|
|
|
| resultsPage.innerHTML = '';
|
|
|
|
|
| const content = document.createElement('div');
|
| content.className = 'results-content';
|
|
|
|
|
| const impostorReveal = document.createElement('div');
|
| impostorReveal.className = 'impostor-reveal';
|
| impostorReveal.textContent = `The impostor was Player ${results.impostor}!`;
|
|
|
|
|
| const votingResults = document.createElement('div');
|
| votingResults.className = 'voting-results';
|
| Object.entries(results.votes).forEach(([player, vote]) => {
|
| const voteEntry = document.createElement('div');
|
| voteEntry.className = 'vote-entry';
|
| voteEntry.textContent = `Player ${player} voted for Player ${vote}`;
|
| votingResults.appendChild(voteEntry);
|
| });
|
|
|
| content.appendChild(impostorReveal);
|
| content.appendChild(votingResults);
|
| resultsPage.appendChild(content);
|
|
|
| this.transitionToPage('results');
|
| }
|
|
|
| handleKeyPress(event) {
|
|
|
| switch (event.key) {
|
| case 'Escape':
|
| this.closeAllOverlays();
|
| break;
|
| case 'r':
|
| if (this.state.currentPage === 'recording') {
|
| this.toggleRecording();
|
| }
|
| break;
|
| }
|
| }
|
|
|
| closeAllOverlays() {
|
| Object.values(this.elements.overlays).forEach(overlay => {
|
| if (overlay) {
|
| overlay.classList.remove('active');
|
| }
|
| });
|
| }
|
|
|
|
|
| cleanup() {
|
|
|
| Object.values(this.elements.buttons).forEach(button => {
|
| if (button) {
|
| button.replaceWith(button.cloneNode(true));
|
| }
|
| });
|
|
|
|
|
| this.closeAllOverlays();
|
| }
|
| }
|
|
|
|
|
| export default GameUI; |