From 28a5affa3363e5d911d2ad0d26adad90962c5b7b Mon Sep 17 00:00:00 2001 From: Frankreed Date: Wed, 3 Apr 2024 11:15:34 -0700 Subject: [PATCH] leaderboard works! --- app/content/drill/[id]/attempts/[attempt].js | 2 +- app/content/drill/[id]/leaderboard.js | 109 +++++++++++-------- app/segments/drill/[id]/submission/input.js | 5 +- db_spec.jsonc | 32 ++++-- hooks/updateLeaderboard.js | 23 ++-- hooks/useLeaderboard.js | 2 +- 6 files changed, 110 insertions(+), 63 deletions(-) diff --git a/app/content/drill/[id]/attempts/[attempt].js b/app/content/drill/[id]/attempts/[attempt].js index 4be67d8f..dbe27d50 100644 --- a/app/content/drill/[id]/attempts/[attempt].js +++ b/app/content/drill/[id]/attempts/[attempt].js @@ -58,7 +58,7 @@ function Result() { Drill Results {Object.keys(drillInfo).length > 0 && - drillInfo["aggOutputs"].map((output) => ( + Object.keys(drillInfo["aggOutputs"]).map((output) => ( { - setManualAttempt( + setManualAttemptCalc( !drillIsLoading && // so that mainOutputAttempt is calculated !leaderboardIsLoading && //leaderboard must've finished loading - (!leaderboard || //and not exist - leaderboard[Object.keys(leaderboard)[0]][mainOutputAttempt]), //or exist but does not have the required field + (!preCalcLeaderboard || //and not exist + preCalcLeaderboard[Object.keys(preCalcLeaderboard)[0]][ + mainOutputAttempt + ] === undefined), //or exist but does not have the required field ); - }, [drillIsLoading, leaderboardIsLoading, leaderboard]); + }, [drillIsLoading, leaderboardIsLoading, preCalcLeaderboard]); // console.log("enabled: ", manualAttempt); @@ -55,7 +58,7 @@ export default function Leaderboard() { error: attemptError, } = useAttempts({ drillId, - enabled: manualAttempt, + enabled: manualAttemptCalc, }); if ( @@ -79,57 +82,75 @@ export default function Leaderboard() { ? drillInfo["mainOutputAttempt"] : customMainOutputAttempt; - const drillLeaderboardAttempts = leaderboard || {}; - if (!leaderboard && attempts) { + const leaderboardAttempts = preCalcLeaderboard || {}; + if (!preCalcLeaderboard && attempts) { //just in case... for (const id in attempts) { const entry = attempts[id]; + + const lowerIsBetter = + drillInfo["aggOutputs"][mainOutputAttempt]["lowerIsBetter"]; // If this uid has not been seen before or the current score is higher, store it if ( - !drillLeaderboardAttempts[entry.uid] || - drillLeaderboardAttempts[entry.uid][mainOutputAttempt] < - entry[mainOutputAttempt] + !leaderboardAttempts[entry.uid] || + (lowerIsBetter && + leaderboardAttempts[entry.uid][mainOutputAttempt]["value"] < + entry[mainOutputAttempt]) || + (!lowerIsBetter && + leaderboardAttempts[entry.uid][mainOutputAttempt]["value"] > + entry[mainOutputAttempt]) ) { - drillLeaderboardAttempts[entry.uid] = entry; + leaderboardAttempts[entry.uid] = { + [mainOutputAttempt]: { + value: entry[mainOutputAttempt], + id: entry.id, + }, + }; } } - console.log("drillLeaderboardAttempts: ", drillLeaderboardAttempts); - - // updateLeaderboard({ - // currentTeamId, - // drillId, - // value: drillLeaderboardAttempts, - // }); + updateLeaderboard({ + currentTeamId, + drillId, + value: leaderboardAttempts, + }); } - const orderedLeaderboard = Object.values(drillLeaderboardAttempts).sort( - (a, b) => a[mainOutputAttempt] - b[mainOutputAttempt], + console.log("drillLeaderboardAttempts: ", leaderboardAttempts); + + const orderedLeaderboard = Object.keys(leaderboardAttempts).sort( + //only sort the userId + (a, b) => + leaderboardAttempts[a][mainOutputAttempt]["value"] - + leaderboardAttempts[b][mainOutputAttempt]["value"], ); return ( - {orderedLeaderboard.map((attempt) => ( - - } - right={() => ( - - {numTrunc(attempt[mainOutputAttempt])} ft - - - )} - /> - - ))} + {orderedLeaderboard.map((userId) => { + const attempt = leaderboardAttempts[userId][mainOutputAttempt]; + return ( + + } + right={() => ( + + {numTrunc(attempt["value"])} ft + + + )} + /> + + ); + })} ); diff --git a/app/segments/drill/[id]/submission/input.js b/app/segments/drill/[id]/submission/input.js index dcbe300e..1bae71c8 100644 --- a/app/segments/drill/[id]/submission/input.js +++ b/app/segments/drill/[id]/submission/input.js @@ -243,8 +243,9 @@ function createOutputData( const aggOutputs = Object.keys(aggOutputsObj); //Generate the aggOutputs for output data - for (let i = 0; i < aggOutputs.length; i++) { - const aggOutput = aggOutputs[i]; + const aggOutputsArr = Object.keys(aggOutputs); + for (let i = 0; i < aggOutputsArr.length; i++) { + const aggOutput = aggOutputsArr[i]; switch (aggOutput) { case "carryDiffAverage": diff --git a/db_spec.jsonc b/db_spec.jsonc index f9c9d7a9..22de4573 100644 --- a/db_spec.jsonc +++ b/db_spec.jsonc @@ -114,13 +114,22 @@ ], }, }, - "best_attempts": { //collection of did - "732489": { //did, document of uid - "2": { //uid, document, contains a map item - "strokesGained": "1237486", //attempt id that contains the "best" score for this field - "proxHole": "1237486" - } - } + "best_attempts": { + //collection of did + "732489": { + //document of did, uid is the key + "2": { + //uid, document, contains a map item + "strokesGained": { + "value": "1237486", //the best value + "id": "567432", //attempt id that contains the "best" score for this field + }, + "proxHole": { + "value": "1237486", + "id": "567432", + }, + }, + }, }, "drills": [ { @@ -170,6 +179,13 @@ "expectedPutts", "strokesGained", ], //all data that will be stored in a shot + "aggOutputs": { + "strokesGained": { "lowerIsBetter": true }, + "strokesGainedAverage": { "lowerIsBetter": true }, + "carryDiffAverage": { "lowerIsBetter": false }, + "sideLandingAverage": { "lowerIsBetter": false }, + "proxHoleAverage": { "lowerIsBetter": false }, + }, //all data that will be stored in an attempt "mainOutputAttempt": "sideLandingAverage", // stat to be used for the barChart in statistics. This is the main data for attempt level data "mainOutputShot": "sideLanding", // stat to be used for the big number in shotAccordion. This is the main data for shot level data "reps": 20, @@ -177,4 +193,4 @@ ], }, }, -} \ No newline at end of file +} diff --git a/hooks/updateLeaderboard.js b/hooks/updateLeaderboard.js index 7cd5c293..7d379174 100644 --- a/hooks/updateLeaderboard.js +++ b/hooks/updateLeaderboard.js @@ -1,13 +1,22 @@ -import { doc, updateDoc } from "firebase/firestore"; -import { db } from "../firebaseConfig"; +import { doc, setDoc, updateDoc } from "firebase/firestore"; +import { db } from "~/firebaseConfig"; export const updateLeaderboard = async ({ currentTeamId, drillId = null, userId = null, + attemptId = null, value = null, }) => { + if (!currentTeamId || !drillId || !value) { + console.log("currentTeamId, drillId, or value not passed in"); + return; + } if (userId) { - //update a user's best after an attempt + //update a user's best after an attempt. Only pass in values to update, don't pass in everything... + if (!attemptId) { + console.log("attemptId or value not passed in"); + return; + } const leaderboardRef = doc( db, "teams", @@ -16,14 +25,14 @@ export const updateLeaderboard = async ({ drillId, ); - const changedObj = {}; + const changedObj = { userId: {} }; Object.keys(value).forEach((key) => { - changedObj[`${userId}.${key}`] = value[key]; + changedObj[userId][key] = { value: value[key], id: attemptId }; }); return updateDoc(leaderboardRef, changedObj); } else { - //update the leaderboard after a new field is added, or initialize the leaderboard + //update the leaderboard after a new field is added, or initialize the leaderboard with a certain field (only 1 field at a time as only 1 field is sorted at one time) const leaderboardRef = doc( db, "teams", @@ -31,6 +40,6 @@ export const updateLeaderboard = async ({ "best_attempts", drillId, ); - return updateDoc(leaderboardRef, value); + return setDoc(leaderboardRef, value, { merge: true }); } }; diff --git a/hooks/useLeaderboard.js b/hooks/useLeaderboard.js index 986e2a84..b168b738 100644 --- a/hooks/useLeaderboard.js +++ b/hooks/useLeaderboard.js @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { doc, getDoc } from "firebase/firestore"; +import { currentAuthContext } from "~/context/Auth"; import { db } from "~/firebaseConfig"; -import { currentAuthContext } from "../context/Auth"; export const useLeaderboard = ({ drillId = null }) => { const { currentTeamId } = currentAuthContext();