const firebase = require('firebase/app'); require('firebase/firestore'); const NUM_SCORES_DISPLAYED = 10; /** * High score with Firebase cloud store. * Index: challengeId ASC difficulty ASC score DESC time ASC */ AFRAME.registerComponent('leaderboard', { schema: { apiKey: {type: 'string'}, authDomain: {type: 'string'}, databaseURL: {type: 'string'}, projectId: {type: 'string'}, storageBucket: {type: 'string'}, messagingSenderId: {type: 'string'}, challengeId: {default: ''}, menuSelectedChallengeId: {default: ''}, isVictory: {default: false} }, init: function () { this.qualifyingIndex = undefined; this.scores = []; this.eventDetail = {scores: this.scores}; this.addEventDetail = {scoreData: undefined, index: undefined}; this.username = localStorage.getItem('supersaberusername') || 'Super Zealot'; this.el.addEventListener('leaderboardusername', evt => { this.username = evt.detail.value; localStorage.setItem('supersaberusername', this.username); }); this.el.addEventListener('leaderboardsubmit', this.addScore.bind(this)); }, update: function (oldData) { // Initialize Cloud Firestore through Firebase. if (!firebase.apps.length && this.data.apiKey) { firebase.initializeApp({ apiKey: this.data.apiKey, authDomain: this.data.authDomain, databaseURL: this.data.databaseURL, projectId: this.data.projectId, storageBucket: this.data.storageBucket, messagingSenderId: this.data.messagingSenderId }); this.firestore = firebase.firestore(); this.firestore.settings({timestampsInSnapshots: true}); this.db = this.firestore.collection('scores'); } if (!oldData.isVictory && this.data.isVictory) { this.checkLeaderboardQualify(); } if (this.data.menuSelectedChallengeId && oldData.menuSelectedChallengeId !== this.data.menuSelectedChallengeId) { this.fetchScores(this.data.menuSelectedChallengeId); } if (this.data.challengeId && oldData.challengeId !== this.data.challengeId) { this.fetchScores(this.data.challengeId); } }, addScore: function () { const state = this.el.sceneEl.systems.state.state; if (!state.isVictory) { return; } const scoreData = { accuracy: state.score.accuracy, challengeId: state.challenge.id, score: state.score.score, username: this.username, difficulty: state.challenge.difficulty, time: new Date() }; this.db.add(scoreData); this.addEventDetail.scoreData = scoreData; this.el.emit('leaderboardscoreadded', this.addEventDetail, false); }, fetchScores: function (challengeId) { const state = this.el.sceneEl.systems.state.state; const query = this.db .where('challengeId', '==', challengeId) .where( 'difficulty', '==', state.menuSelectedChallenge.id ? state.menuSelectedChallenge.difficulty : state.challenge.difficulty) .orderBy('score', 'desc') .orderBy('time', 'asc') .limit(10); query.get().then(snapshot => { this.eventDetail.challengeId = challengeId; this.scores.length = 0; if (!snapshot.empty) { snapshot.forEach(score => this.scores.push(score.data())); } this.el.sceneEl.emit('leaderboard', this.eventDetail, false); }).catch(e => { console.error('[firestore]', e); }); }, /** * Is high score? */ checkLeaderboardQualify: function () { const state = this.el.sceneEl.systems.state.state; const score = state.score.score; // If less than 10, then automatic high score. if (this.scores.length < NUM_SCORES_DISPLAYED) { this.qualifyingIndex = this.scores.length; this.el.sceneEl.emit('leaderboardqualify', this.scores.length, false); return; } // Check if overtook any existing high score. for (let i = 0; i < this.scores.length; i++) { if (score > this.scores[i].score) { this.qualifyingIndex = i; this.el.sceneEl.emit('leaderboardqualify', i, false); return; } } } }); AFRAME.registerComponent('leaderboard-title', { schema: { leaderboardQualified: {default: false} }, update: function () { const value = this.data.leaderboardQualified ? 'NEW HIGH SCORE!' : 'LEADERBOARD'; this.el.setAttribute('text', 'value', value); } });