Skip to content

Commit

Permalink
leaderboard works!
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankreedX committed Apr 4, 2024
1 parent 51a4929 commit 28a5aff
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 63 deletions.
2 changes: 1 addition & 1 deletion app/content/drill/[id]/attempts/[attempt].js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function Result() {
<Text style={styles.sectionTitle}>Drill Results</Text>

{Object.keys(drillInfo).length > 0 &&
drillInfo["aggOutputs"].map((output) => (
Object.keys(drillInfo["aggOutputs"]).map((output) => (
<View
style={{
flexDirection: "row",
Expand Down
109 changes: 65 additions & 44 deletions app/content/drill/[id]/leaderboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { Avatar, Icon, List, Text } from "react-native-paper";
import { numTrunc } from "~/Utility";
import ErrorComponent from "~/components/errorComponent";
import Loading from "~/components/loading";
import { currentAuthContext } from "~/context/Auth";
import { updateLeaderboard } from "~/hooks/updateLeaderboard";
import { useAttempts } from "~/hooks/useAttempts";
import { useDrillInfo } from "~/hooks/useDrillInfo";
import { useLeaderboard } from "~/hooks/useLeaderboard";
import { useUserInfo } from "~/hooks/useUserInfo";
import { currentAuthContext } from "../../../../context/Auth";
import { useLeaderboard } from "../../../../hooks/useLeaderboard";

export default function Leaderboard() {
const { currentTeamId } = currentAuthContext();
Expand All @@ -18,7 +19,7 @@ export default function Leaderboard() {
const [defaultMainOutputAttempt, setDefaultMainOutputAttempt] =
useState(true); //whether mainOutputAttempt is the default set on drills or has been changed by user
const [customMainOutputAttempt, setCustomMainOutputAttempt] = useState("did"); //What is the custom mainOutputAttempt in case defaultMainOutputAttempt is false
const [manualAttempt, setManualAttempt] = useState(false); //whether the user has manually set the mainOutputAttempt
const [manualAttemptCalc, setManualAttemptCalc] = useState(false); //whether the attempt is manually calculated or grabbed from precalculated leaderboard

const {
data: userInfo,
Expand All @@ -33,19 +34,21 @@ export default function Leaderboard() {
} = useDrillInfo(drillId);

const {
data: leaderboard,
data: preCalcLeaderboard,
isLoading: leaderboardIsLoading,
error: leaderboardError,
} = useLeaderboard({ drillId });

useEffect(() => {
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);

Expand All @@ -55,7 +58,7 @@ export default function Leaderboard() {
error: attemptError,
} = useAttempts({
drillId,
enabled: manualAttempt,
enabled: manualAttemptCalc,
});

if (
Expand All @@ -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 (
<ScrollView>
<List.Section style={{ marginLeft: 20 }}>
{orderedLeaderboard.map((attempt) => (
<Link
key={attempt["uid"]}
href={{
pathname: `${currentPath}/attempts/${attempt["id"]}`,
}}
asChild
>
<List.Item
title={userInfo[attempt["uid"]]["name"]}
left={() => <Avatar.Text size={24} label="XD" />}
right={() => (
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Text>{numTrunc(attempt[mainOutputAttempt])} ft</Text>
<Icon source="chevron-right" />
</View>
)}
/>
</Link>
))}
{orderedLeaderboard.map((userId) => {
const attempt = leaderboardAttempts[userId][mainOutputAttempt];
return (
<Link
key={userId}
href={{
pathname: `${currentPath}/attempts/${attempt["id"]}`,
}}
asChild
>
<List.Item
title={userInfo[userId]["name"]}
left={() => <Avatar.Text size={24} label="XD" />}
right={() => (
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Text>{numTrunc(attempt["value"])} ft</Text>
<Icon source="chevron-right" />
</View>
)}
/>
</Link>
);
})}
</List.Section>
</ScrollView>
);
Expand Down
5 changes: 3 additions & 2 deletions app/segments/drill/[id]/submission/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
32 changes: 24 additions & 8 deletions db_spec.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down Expand Up @@ -170,11 +179,18 @@
"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,
},
],
},
},
}
}
23 changes: 16 additions & 7 deletions hooks/updateLeaderboard.js
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -16,21 +25,21 @@ 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",
currentTeamId,
"best_attempts",
drillId,
);
return updateDoc(leaderboardRef, value);
return setDoc(leaderboardRef, value, { merge: true });
}
};
2 changes: 1 addition & 1 deletion hooks/useLeaderboard.js
Original file line number Diff line number Diff line change
@@ -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();
Expand Down

0 comments on commit 28a5aff

Please sign in to comment.