From a502aa9b6c60014d27b174718d5b1495ded379de Mon Sep 17 00:00:00 2001 From: hannacol Date: Sun, 7 Apr 2024 15:20:30 -0700 Subject: [PATCH 01/11] Database updated and codebase uses title instead of drillType --- app/content/drill/[id]/description.js | 52 ++++++++++----------- app/content/drill/[id]/index.js | 2 +- app/content/drill/index.js | 18 ++++++- app/segments/drill/[id]/submission/input.js | 2 +- components/drillCard.js | 4 +- db_spec.jsonc | 3 +- drill_data.json | 6 ++- 7 files changed, 52 insertions(+), 35 deletions(-) diff --git a/app/content/drill/[id]/description.js b/app/content/drill/[id]/description.js index ab461ea8..170463fe 100644 --- a/app/content/drill/[id]/description.js +++ b/app/content/drill/[id]/description.js @@ -60,13 +60,11 @@ export default function Description() { }; const images = [ - require("~/assets/drill-description-image.jpg"), - require("~/assets/adaptive-icon.png"), - require("~/assets/icon.png"), - require("~/assets/splash.png"), - require("~/assets/favicon.png"), + ]; + const hasImages = !!images.length; + const windowWidth = Dimensions.get("window").width; const windowHeight = Dimensions.get("window").height; @@ -90,28 +88,30 @@ export default function Description() { Description {drillInfo["description"]} - - - {images.map((image, index) => ( - openModal(index)}> - - - ))} + {hasImages && + + + {images.map((image, index) => ( + openModal(index)}> + + + ))} + - + } - + {/* Tab system */} diff --git a/app/content/drill/index.js b/app/content/drill/index.js index ee694946..4471299b 100644 --- a/app/content/drill/index.js +++ b/app/content/drill/index.js @@ -24,6 +24,20 @@ export default function Index() { return ; } + const drills = Object.values(drillInfo); + + drills.sort((a, b) => { + const titleA = a.title.toUpperCase(); + const titleB = b.title.toUpperCase(); + if (titleA < titleB) { + return -1; + } + if (titleA > titleB) { + return 1; + } + return 0; + }); + return ( @@ -33,7 +47,7 @@ export default function Index() { - {Object.values(drillInfo).map((drill) => ( + {Object.values(drills).map((drill) => ( - {props.drill["drillType"]} + {props.drill["title"]} {props.drill["spec"]} @@ -54,7 +54,7 @@ const styles = StyleSheet.create({ justifyContent: "space-between", alignItems: "center", }, - drillType: { + title: { fontSize: 18, fontWeight: "bold", color: "#333", diff --git a/db_spec.jsonc b/db_spec.jsonc index 22de4573..e48ca0a8 100644 --- a/db_spec.jsonc +++ b/db_spec.jsonc @@ -134,7 +134,7 @@ "drills": [ { "did": "732489", // drillInfo id - "drillType": "20 Shot Challenge", // e.g. "20 shot challenge", "putting challenge", "line test" + "drillType": "20ShotChallenge", // e.g. "20 shot challenge", "putting challenge", "line test" "spec": "50ft - 150ft", // this is just a string shown to the user. TODO: Add numeric fields for random distance ranges "shotType": "arg", // e.g. "arg", "putt", "ott", "app" "requirements": [ @@ -168,6 +168,7 @@ ], }, ], + "title": "20 Shot Challenge", "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", "inputs": ["distance", "sideLanding"], "outputs": [ diff --git a/drill_data.json b/drill_data.json index 84ecca9d..6a664b35 100644 --- a/drill_data.json +++ b/drill_data.json @@ -68374,7 +68374,8 @@ "drills": { "SpvYyY94HaulVH2zmVyM": { "did": "SpvYyY94HaulVH2zmVyM", - "drillType": "20 Shot Challenge", + "title": "20 Shot Challenge", + "drillType": "20ShotChallenge", "spec": "50ft - 150ft", "shotType": "app", "requirements": [ @@ -68402,7 +68403,8 @@ }, "YtCsaxzscFScnpZYmnKI": { "did": "YtCsaxzscFScnpZYmnKI", - "drillType": "Line Test", + "title": "Line Test", + "drillType": "lineTest", "spec": "7 – PW", "shotType": "app", "requirements": [ From 083275504214525a1348630cb40170cb19f49c65 Mon Sep 17 00:00:00 2001 From: hannacol Date: Mon, 8 Apr 2024 13:21:53 -0700 Subject: [PATCH 02/11] Removed title and added prettyDrillType and subType --- Utility.js | 4 ++++ app/content/drill/[id]/index.js | 3 ++- app/content/drill/index.js | 7 ++++--- app/segments/drill/[id]/submission/input.js | 3 ++- app/segments/drill/[id]/submission/result.js | 5 +++-- components/drillCard.js | 12 ++++++------ db_spec.jsonc | 3 ++- drill_data.json | 12 +++++++----- 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Utility.js b/Utility.js index 60fa45da..7f28937a 100644 --- a/Utility.js +++ b/Utility.js @@ -169,3 +169,7 @@ export function getIconByKey(key) { const iconObject = icons.find((icon) => icon[key]); return iconObject ? iconObject[key] : null; } + +export const getDrillTitle = (drill) => { + return `${drill.prettyDrillType} | ${drill.subType}`; +} \ No newline at end of file diff --git a/app/content/drill/[id]/index.js b/app/content/drill/[id]/index.js index 9d9aad47..c280e6be 100644 --- a/app/content/drill/[id]/index.js +++ b/app/content/drill/[id]/index.js @@ -10,6 +10,7 @@ import { SafeAreaView } from "react-native-safe-area-context"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import { useDrillInfo } from "~/hooks/useDrillInfo"; +import { getDrillTitle } from "~/Utility" export default function Index() { const [value, setValue] = React.useState("description"); @@ -48,7 +49,7 @@ export default function Index() { }} color={"#F24E1E"} /> - + {/* Tab system */} diff --git a/app/content/drill/index.js b/app/content/drill/index.js index 4471299b..64e963db 100644 --- a/app/content/drill/index.js +++ b/app/content/drill/index.js @@ -6,6 +6,7 @@ import { SafeAreaView } from "react-native-safe-area-context"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import { useDrillInfo } from "~/hooks/useDrillInfo"; +import { getDrillTitle } from "~/Utility" export default function Index() { const navigation = useNavigation(); @@ -27,8 +28,8 @@ export default function Index() { const drills = Object.values(drillInfo); drills.sort((a, b) => { - const titleA = a.title.toUpperCase(); - const titleB = b.title.toUpperCase(); + const titleA = getDrillTitle(a).toUpperCase(); + const titleB = getDrillTitle(b).toUpperCase(); if (titleA < titleB) { return -1; } @@ -57,7 +58,7 @@ export default function Index() { style={{ paddingVertical: 8 }} > - + diff --git a/components/drillCard.js b/components/drillCard.js index 66e07cce..5b9d6ffd 100644 --- a/components/drillCard.js +++ b/components/drillCard.js @@ -1,5 +1,6 @@ import { Link } from "expo-router"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { getDrillTitle } from "~/Utility" function DrillCard(props) { console.log(props); @@ -7,15 +8,14 @@ function DrillCard(props) { - {props.drill["title"]} + {getDrillTitle(props.drill)} - {props.drill["spec"]} {props.drill["inputs"] .map((input) => { let retVal = ""; - switch (input) { - case "distance": + switch (input.id) { + case "carry": retVal = "↑"; break; case "sideLanding": @@ -46,7 +46,7 @@ const styles = StyleSheet.create({ borderColor: "#ddd", borderRadius: 8, marginVertical: 5, - padding: 10, + padding: 15, backgroundColor: "#fff", }, cardContent: { @@ -55,7 +55,7 @@ const styles = StyleSheet.create({ alignItems: "center", }, title: { - fontSize: 18, + fontSize: 16, fontWeight: "bold", color: "#333", }, diff --git a/db_spec.jsonc b/db_spec.jsonc index e48ca0a8..c47bed91 100644 --- a/db_spec.jsonc +++ b/db_spec.jsonc @@ -168,7 +168,8 @@ ], }, ], - "title": "20 Shot Challenge", + "prettyDrillType": "20 Shot Challenge", + "subType": "Red Zone", "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", "inputs": ["distance", "sideLanding"], "outputs": [ diff --git a/drill_data.json b/drill_data.json index 6a664b35..8133289b 100644 --- a/drill_data.json +++ b/drill_data.json @@ -68374,8 +68374,9 @@ "drills": { "SpvYyY94HaulVH2zmVyM": { "did": "SpvYyY94HaulVH2zmVyM", - "title": "20 Shot Challenge", "drillType": "20ShotChallenge", + "prettyDrillType": "20 Shot Challenge", + "subType": "Red Zone", "spec": "50ft - 150ft", "shotType": "app", "requirements": [ @@ -68387,7 +68388,7 @@ } ], "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", - "inputs": ["distance", "sideLanding"], + "inputs": [ "distance", "sideLanding" ], "outputs": [ "target", "carry", @@ -68403,8 +68404,9 @@ }, "YtCsaxzscFScnpZYmnKI": { "did": "YtCsaxzscFScnpZYmnKI", - "title": "Line Test", "drillType": "lineTest", + "prettyDrillType": "Line Test", + "subType": "7 - PW", "spec": "7 – PW", "shotType": "app", "requirements": [ @@ -68436,8 +68438,8 @@ } ], "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", - "inputs": ["sideLanding"], - "outputs": ["target", "sideLanding"], + "inputs": [ "sideLanding" ], + "outputs": [ "target", "sideLanding" ], "mainOutputAttempt": "sideLandingAverage", "mainOutputShot": "sideLanding", "reps": 20 From d2f56fc5fdca74c9944fb172ebde203938886b20 Mon Sep 17 00:00:00 2001 From: hannacol Date: Mon, 8 Apr 2024 22:51:44 -0700 Subject: [PATCH 03/11] Added putting to database and updated codebase --- Utility.js | 4 +- app/segments/drill/[id]/submission/input.js | 107 +++++++++++++------- components/shotAccordion.js | 6 +- 3 files changed, 73 insertions(+), 44 deletions(-) diff --git a/Utility.js b/Utility.js index 7f28937a..43644acf 100644 --- a/Utility.js +++ b/Utility.js @@ -164,12 +164,12 @@ export function lookUpExpectedPutts(proxHole) { } export function getIconByKey(key) { - const icons = [{ carry: "arrow-up" }, { sideLanding: "arrow-left-right" }]; + const icons = [{ carry: "arrow-up" }, { sideLanding: "arrow-left-right" }, { strokes: "golf-tee" }]; const iconObject = icons.find((icon) => icon[key]); return iconObject ? iconObject[key] : null; } export const getDrillTitle = (drill) => { - return `${drill.prettyDrillType} | ${drill.subType}`; + return `${drill.prettyDrillType} | ${drill.subType}`; } \ No newline at end of file diff --git a/app/segments/drill/[id]/submission/input.js b/app/segments/drill/[id]/submission/input.js index 213a0b8a..9eb39e74 100644 --- a/app/segments/drill/[id]/submission/input.js +++ b/app/segments/drill/[id]/submission/input.js @@ -135,34 +135,36 @@ async function uploadAttempt( ***************************************/ function getShotInfo(drillInfo) { let shots = []; - for (let i = 0; i < drillInfo.requirements.length; i++) { - switch (drillInfo.requirements[i].type) { - case "random": - shots = fillRandomShotTargets( - drillInfo.requirements[i].min, - drillInfo.requirements[i].max, - drillInfo, - ); - break; - case "sequence": - shots = fillClubTargets(i, drillInfo); - break; - default: - console.log("Shots not found"); - shots = []; - break; - } + switch (drillInfo.requirements[0].type) { + case "random": + shots = fillRandomShotTargets( + drillInfo.requirements[0].min, + drillInfo.requirements[0].max, + drillInfo, + ); + break; + case "sequence": + shots = fillClubTargets(drillInfo); + break; + case "putt": + shots = fillPuttTargets(drillInfo); + break; + default: + console.log("Shots not found"); + shots = []; + break; } + return shots; } //Helper function for the sequence drill type -function fillClubTargets(idx, drillInfo) { +function fillClubTargets(drillInfo) { let shots = []; for (var i = 0; i < drillInfo.reps; i++) { shots.push({ shotNum: i + 1, - target: drillInfo.requirements[idx].items[i], + target: drillInfo.requirements[0].items[i], }); } return shots; @@ -188,6 +190,23 @@ function fillRandomShotTargets(min, max, drillInfo) { return shots; } +//Helper function for the putt drill type +function fillPuttTargets(drillInfo) { + let shots = []; + for (var i = 0; i < drillInfo.reps; i++) { + var baseline = lookUpExpectedPutts(drillInfo.requirements[0].items[i]); + shots.push({ + shotNum: i + 1, + target: [ + drillInfo.requirements[0].items[i], + drillInfo.requirements[1].items[i], + ], + baseline: baseline, + }); + } + return shots; +} + /*************************************** * Output Data Generation ***************************************/ @@ -204,12 +223,11 @@ function calculateCarryDiff(target, carry) { //Function to create and format output data function createOutputData( + drillInfo, inputValues, attemptShots, uid, did, - outputs, - aggOutputsObj, ) { //initialize total values let strokesGainedTotal = 0; @@ -223,12 +241,12 @@ function createOutputData( for (let j = 0; j < inputValues.length; j++) { //Generate the shots array for output data let shot = {}; - for (let i = 0; i < outputs.length; i++) { - const output = outputs[i]; + for (let i = 0; i < drillInfo.outputs.length; i++) { + const output = drillInfo.outputs[i]; switch (output) { case "target": - shot.target = attemptShots[j].target; + shot.target = attemptShots[j].target[0]; break; case "carry": @@ -268,16 +286,27 @@ function createOutputData( break; case "strokesGained": - shot.strokesGained = - attemptShots[j].baseline - - lookUpExpectedPutts( - calculateProxHole( - attemptShots[j].target, - inputValues[j].carry, - inputValues[j].sideLanding, - ), - ); - -1; + switch (drillInfo.shotType) { + case "app": + shot.strokesGained = + attemptShots[j].baseline - + lookUpExpectedPutts( + calculateProxHole( + attemptShots[j].target, + inputValues[j].carry, + inputValues[j].sideLanding, + ), + ); + -1; + break; + case "putt": + shot.strokesGained = attemptShots.baseline - inputValues[j].strokes; + break; + default: + console.log("Shot type does not exist."); + break; + } + strokesGainedTotal += shot.strokesGained; break; @@ -314,7 +343,7 @@ function createOutputData( }; //Generate the aggOutputs for output data - const aggOutputsArr = Object.keys(aggOutputsObj); + const aggOutputsArr = Object.keys(drillInfo.aggOutputs); for (let i = 0; i < aggOutputsArr.length; i++) { const aggOutput = aggOutputsArr[i]; @@ -416,12 +445,11 @@ export default function Input({ drillInfo, setToggleResult, setOutputData }) { mode="contained-tonal" onPress={() => { let outputData = createOutputData( + drillInfo, inputValues, attemptShots, currentUserId, did, - drillInfo.outputs, - drillInfo.aggOutputs, ); setOutputData(outputData); @@ -434,6 +462,7 @@ export default function Input({ drillInfo, setToggleResult, setOutputData }) { queryClient, ); setToggleResult(true); + }} > Submit Drill @@ -559,7 +588,7 @@ export default function Input({ drillInfo, setToggleResult, setOutputData }) { key={id} prompt={item.prompt} distanceMeasure={item.distanceMeasure} - target={attemptShots[displayedShot].target} + target={attemptShots[displayedShot].target[id]} /> ))} @@ -661,7 +690,7 @@ export default function Input({ drillInfo, setToggleResult, setOutputData }) { for (let i = 0; i < attemptShots.length; i++) { drillInfo.inputs.forEach((item) => { newInputValues[i][item.id] = Math.floor( - Math.random() * attemptShots[displayedShot].target, + Math.random() * attemptShots[displayedShot].target[0], ).toString(); }); } diff --git a/components/shotAccordion.js b/components/shotAccordion.js index 3e2187ef..29afbdb8 100644 --- a/components/shotAccordion.js +++ b/components/shotAccordion.js @@ -84,15 +84,15 @@ function ShotAccordion(props) { width: "100%", }} > - + Shot: {props.shot["sid"]}/ {props.total} - + Target:{" "} {props.shot["target"]} yd - + SG:{" "} {numTrunc(props.shot[props.drillInfo["mainOutputShot"]])} From 742e1b9889ef2f32aee6009f20853c511af14cfa Mon Sep 17 00:00:00 2001 From: hannacol Date: Tue, 9 Apr 2024 08:01:31 -0700 Subject: [PATCH 04/11] Rebased with layout and prettiier ran --- Utility.js | 8 ++++++-- app/content/drill/[id]/description.js | 13 +++++++------ app/content/drill/[id]/index.js | 2 +- app/content/drill/index.js | 2 +- app/segments/drill/[id]/submission/input.js | 19 +++++++------------ app/segments/drill/[id]/submission/result.js | 2 +- components/drillCard.js | 2 +- drill_data.json | 6 +++--- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Utility.js b/Utility.js index 43644acf..b42f4287 100644 --- a/Utility.js +++ b/Utility.js @@ -164,7 +164,11 @@ export function lookUpExpectedPutts(proxHole) { } export function getIconByKey(key) { - const icons = [{ carry: "arrow-up" }, { sideLanding: "arrow-left-right" }, { strokes: "golf-tee" }]; + const icons = [ + { carry: "arrow-up" }, + { sideLanding: "arrow-left-right" }, + { strokes: "golf-tee" }, + ]; const iconObject = icons.find((icon) => icon[key]); return iconObject ? iconObject[key] : null; @@ -172,4 +176,4 @@ export function getIconByKey(key) { export const getDrillTitle = (drill) => { return `${drill.prettyDrillType} | ${drill.subType}`; -} \ No newline at end of file +}; diff --git a/app/content/drill/[id]/description.js b/app/content/drill/[id]/description.js index 170463fe..e15da1ba 100644 --- a/app/content/drill/[id]/description.js +++ b/app/content/drill/[id]/description.js @@ -59,9 +59,7 @@ export default function Description() { ); }; - const images = [ - - ]; + const images = []; const hasImages = !!images.length; @@ -88,7 +86,7 @@ export default function Description() { Description {drillInfo["description"]} - {hasImages && + {hasImages && ( {images.map((image, index) => ( - openModal(index)}> + openModal(index)} + > - } + )} Submit Drill @@ -690,7 +684,8 @@ export default function Input({ drillInfo, setToggleResult, setOutputData }) { for (let i = 0; i < attemptShots.length; i++) { drillInfo.inputs.forEach((item) => { newInputValues[i][item.id] = Math.floor( - Math.random() * attemptShots[displayedShot].target[0], + Math.random() * + attemptShots[displayedShot].target[0], ).toString(); }); } diff --git a/app/segments/drill/[id]/submission/result.js b/app/segments/drill/[id]/submission/result.js index d03cde70..fdf29c18 100644 --- a/app/segments/drill/[id]/submission/result.js +++ b/app/segments/drill/[id]/submission/result.js @@ -3,7 +3,7 @@ import { StyleSheet } from "react-native"; import { Appbar } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import ResultScreen from "~/components/resultScreen"; -import { getDrillTitle } from "~/Utility" +import { getDrillTitle } from "~/Utility"; function Result(props) { const submission = props.submission; diff --git a/components/drillCard.js b/components/drillCard.js index 5b9d6ffd..e9cbf65f 100644 --- a/components/drillCard.js +++ b/components/drillCard.js @@ -1,6 +1,6 @@ import { Link } from "expo-router"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { getDrillTitle } from "~/Utility" +import { getDrillTitle } from "~/Utility"; function DrillCard(props) { console.log(props); diff --git a/drill_data.json b/drill_data.json index 8133289b..92e31331 100644 --- a/drill_data.json +++ b/drill_data.json @@ -68388,7 +68388,7 @@ } ], "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", - "inputs": [ "distance", "sideLanding" ], + "inputs": ["distance", "sideLanding"], "outputs": [ "target", "carry", @@ -68438,8 +68438,8 @@ } ], "description": "Adipisicing ad sint occaecat magna sint ad ea laboris voluptate fugiat labore. Aute labore anim occaecat laborum in magna dolore dolor cupidatat. Ea excepteur nulla magna eiusmod ad. Aute tempor ut cillum laborum ea commodo est ad non et duis tempor exercitation ad. Aute deserunt aliquip exercitation.", - "inputs": [ "sideLanding" ], - "outputs": [ "target", "sideLanding" ], + "inputs": ["sideLanding"], + "outputs": ["target", "sideLanding"], "mainOutputAttempt": "sideLandingAverage", "mainOutputShot": "sideLanding", "reps": 20 From be4b8a9cac1d57aa245266679e6a8dd8a59ed538 Mon Sep 17 00:00:00 2001 From: Frankreed Date: Tue, 9 Apr 2024 10:50:00 -0700 Subject: [PATCH 05/11] leaderboard is more resilient to incorrect data --- app/content/drill/[id]/leaderboard.js | 13 ++++++++++--- components/barChart.js | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/content/drill/[id]/leaderboard.js b/app/content/drill/[id]/leaderboard.js index a633d331..7f676ad0 100644 --- a/app/content/drill/[id]/leaderboard.js +++ b/app/content/drill/[id]/leaderboard.js @@ -39,11 +39,14 @@ export default function Leaderboard() { error: leaderboardError, } = useLeaderboard({ drillId }); + const preCalcLeaderboardExists = + preCalcLeaderboard && Object.keys(preCalcLeaderboard).length > 0; + useEffect(() => { setManualAttemptCalc( !drillIsLoading && // so that mainOutputAttempt is calculated !leaderboardIsLoading && //leaderboard must've finished loading - (!preCalcLeaderboard || //and not exist + (!preCalcLeaderboardExists || //and not exist preCalcLeaderboard[Object.keys(preCalcLeaderboard)[0]][ mainOutputAttempt ] === undefined), //or exist but does not have the required field @@ -83,7 +86,7 @@ export default function Leaderboard() { : customMainOutputAttempt; const leaderboardAttempts = preCalcLeaderboard || {}; - if (!preCalcLeaderboard && attempts) { + if (!preCalcLeaderboardExists && attempts) { //just in case... for (const id in attempts) { const entry = attempts[id]; @@ -125,6 +128,10 @@ export default function Leaderboard() { leaderboardAttempts[b][mainOutputAttempt]["value"], ); + if (orderedLeaderboard.length < 1) { + return No attempts have been made yet.; + } + return ( @@ -139,7 +146,7 @@ export default function Leaderboard() { asChild > } right={() => ( diff --git a/components/barChart.js b/components/barChart.js index 2af7dbdc..9a710506 100644 --- a/components/barChart.js +++ b/components/barChart.js @@ -17,7 +17,7 @@ import ShotAccordion from "~/components/shotAccordion"; export default function BarChartScreen({ drillData, drillInfo }) { if (drillData.length === 0) { - return Loading...; + return No attempts have been made yet.; } const drillDataSorted = drillData.sort((a, b) => a.time - b.time); const data = drillDataSorted.map( From 3de3c55d2b8f7db89763aa89dca0713eb6e3ca4e Mon Sep 17 00:00:00 2001 From: Frankreed Date: Tue, 9 Apr 2024 11:23:01 -0700 Subject: [PATCH 06/11] style changes --- Utility.js | 4 ++-- app/content/drill/[id]/index.js | 4 ++-- app/content/drill/index.js | 8 ++++---- app/segments/drill/[id]/submission/input.js | 18 +++++++----------- app/segments/drill/[id]/submission/result.js | 4 ++-- components/drillCard.js | 4 ++-- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Utility.js b/Utility.js index b42f4287..ff51373d 100644 --- a/Utility.js +++ b/Utility.js @@ -174,6 +174,6 @@ export function getIconByKey(key) { return iconObject ? iconObject[key] : null; } -export const getDrillTitle = (drill) => { - return `${drill.prettyDrillType} | ${drill.subType}`; +export const getCombinedDrillTitle = (drillInfo) => { + return `${drillInfo.prettyDrillType} | ${drillInfo.subType}`; }; diff --git a/app/content/drill/[id]/index.js b/app/content/drill/[id]/index.js index 586ad440..a68b09f4 100644 --- a/app/content/drill/[id]/index.js +++ b/app/content/drill/[id]/index.js @@ -10,7 +10,7 @@ import { SafeAreaView } from "react-native-safe-area-context"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import { useDrillInfo } from "~/hooks/useDrillInfo"; -import { getDrillTitle } from "~/Utility"; +import { getCombinedDrillTitle } from "~/Utility"; export default function Index() { const [value, setValue] = React.useState("description"); @@ -49,7 +49,7 @@ export default function Index() { }} color={"#F24E1E"} /> - + {/* Tab system */} diff --git a/app/content/drill/index.js b/app/content/drill/index.js index 7e6da415..33cf878d 100644 --- a/app/content/drill/index.js +++ b/app/content/drill/index.js @@ -6,7 +6,7 @@ import { SafeAreaView } from "react-native-safe-area-context"; import ErrorComponent from "~/components/errorComponent"; import Loading from "~/components/loading"; import { useDrillInfo } from "~/hooks/useDrillInfo"; -import { getDrillTitle } from "~/Utility"; +import { getCombinedDrillTitle } from "~/Utility"; export default function Index() { const navigation = useNavigation(); @@ -28,8 +28,8 @@ export default function Index() { const drills = Object.values(drillInfo); drills.sort((a, b) => { - const titleA = getDrillTitle(a).toUpperCase(); - const titleB = getDrillTitle(b).toUpperCase(); + const titleA = getCombinedDrillTitle(a).toUpperCase(); + const titleB = getCombinedDrillTitle(b).toUpperCase(); if (titleA < titleB) { return -1; } @@ -58,7 +58,7 @@ export default function Index() { style={{ paddingVertical: 8 }} > - + diff --git a/components/drillCard.js b/components/drillCard.js index e9cbf65f..63786e0a 100644 --- a/components/drillCard.js +++ b/components/drillCard.js @@ -1,6 +1,6 @@ import { Link } from "expo-router"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { getDrillTitle } from "~/Utility"; +import { getCombinedDrillTitle } from "~/Utility"; function DrillCard(props) { console.log(props); @@ -8,7 +8,7 @@ function DrillCard(props) { - {getDrillTitle(props.drill)} + {getCombinedDrillTitle(props.drill)} {props.drill["inputs"] From 75f2a7dc5b884bbb6611d44c61c3eb79a61eee84 Mon Sep 17 00:00:00 2001 From: Frankreed Date: Fri, 5 Apr 2024 14:29:46 -0700 Subject: [PATCH 07/11] barChart performance vastly improved. Frame rate is now pegged at 60 (for ios). Still crashes on Android. --- app/content/_layout.js | 3 -- components/barChart.js | 79 +++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 50 deletions(-) diff --git a/app/content/_layout.js b/app/content/_layout.js index 6bd137ae..95d26d80 100644 --- a/app/content/_layout.js +++ b/app/content/_layout.js @@ -1,10 +1,7 @@ import { Tabs } from "expo-router"; import Icon from "react-native-vector-icons/MaterialCommunityIcons"; -import { currentAuthContext } from "~/context/Auth"; export default () => { - const { currentUserId } = currentAuthContext(); - console.log("user", currentUserId); return ( No attempts have been made yet.; } - const drillDataSorted = drillData.sort((a, b) => a.time - b.time); - const data = drillDataSorted.map( - (value) => value[drillInfo["mainOutputAttempt"]], - ); - - console.log("drillDataSorted", drillData); + const data = useMemo(() => { + return drillData + .sort((a, b) => a.time - b.time) + .map((value) => value[drillInfo["mainOutputAttempt"]]); + }, [drillData, drillInfo]); - const [_, setScrollPosition] = useState(0); const [movingAvgRange, setMovingAvgRange] = useState(5); const [movingAvgRangeValues] = useState([ { label: "3", value: 3 }, @@ -38,11 +36,7 @@ export default function BarChartScreen({ drillData, drillInfo }) { useState(false); const { width } = useWindowDimensions(); - const fill = "rgb(134, 65, 244)"; const [selected, setSelected] = useState(0); - const scrollViewRef = useRef(); - - const dateString = formatDate(drillDataSorted[selected]["time"]); const barWidth = 50; @@ -94,20 +88,19 @@ export default function BarChartScreen({ drillData, drillInfo }) { .y((d) => scaleY(d))(movingAvgData); const handleScroll = function (event) { - setScrollPosition(event.nativeEvent.contentOffset.x); setSelected(selectedBar(event.nativeEvent.contentOffset.x)); }; - const MovingAvgPath = function MovingAvgPath({ line }) { - return ( - { + return drillData[selected]["shots"].map((shot) => ( + - ); - }; + )); + }, [drillData, drillInfo, selected]); const styles = StyleSheet.create({ movingAvgContainer: { @@ -137,10 +130,6 @@ export default function BarChartScreen({ drillData, drillInfo }) { borderRadius: 4, paddingHorizontal: 10, }, - chartSection: { - marginTop: 20, - marginBottom: 20, - }, yAxis: { position: "absolute", top: 0, @@ -149,9 +138,8 @@ export default function BarChartScreen({ drillData, drillInfo }) { left: 0, height: chartHeight, zIndex: 5, - backgroundColor: "#F4F4F4", // Set background color + backgroundColor: "#F2F2F2", // Set background color paddingHorizontal: 5, // Add padding - borderRadius: 10, // Add border radius for rounded corners }, middleLine: { position: "absolute", @@ -192,7 +180,6 @@ export default function BarChartScreen({ drillData, drillInfo }) { color: "#333", // Adjust text color }, }); - return ( @@ -212,13 +199,7 @@ export default function BarChartScreen({ drillData, drillInfo }) { `${value}`} // Format label as needed /> @@ -226,15 +207,15 @@ export default function BarChartScreen({ drillData, drillInfo }) { - {dateString} + {formatDate(drillData[selected]["time"])} - {drillDataSorted[selected]["shots"].map((shot) => ( - - ))} + {shotAccordionList} ); } + +const MovingAvgPath = ({ line }) => { + return ( + + ); +}; From ccd2e9d8eb67a823c815348621b41eb27454449a Mon Sep 17 00:00:00 2001 From: Frankreed Date: Sun, 7 Apr 2024 09:35:29 -0700 Subject: [PATCH 08/11] this doesn't improve anything measurable, it just makes sense in my head --- components/barChart.js | 95 +++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/components/barChart.js b/components/barChart.js index 7497e722..e30b0a83 100644 --- a/components/barChart.js +++ b/components/barChart.js @@ -52,55 +52,72 @@ export default function BarChartScreen({ drillData, drillInfo }) { ); }; - const processedData = useMemo(() => { - return data.map((value, index) => ({ - value: value, - index: index, - svg: { - fill: value > 0 ? "green" : "red", - }, - })); - }, [data]); + const processedData = useMemo( + () => + data.map((value, index) => ({ + value: value, + index: index, + svg: { + fill: value > 0 ? "green" : "red", + }, + })), + [data], + ); - const movingAvgData = data.map((value, index) => - index + 1 >= movingAvgRange - ? data - .slice(index - movingAvgRange + 1, index + 1) - .reduce((a, b) => a + b, 0) / movingAvgRange - : 0, + const movingAvgData = useMemo( + () => + data.map((value, index) => + index + 1 >= movingAvgRange + ? data + .slice(index - movingAvgRange + 1, index + 1) + .reduce((a, b) => a + b, 0) / movingAvgRange + : 0, + ), + [data, movingAvgRange], ); // Calculate scales - const scaleY = scale - .scaleLinear() - .domain([Math.min(...data), Math.max(...data)]) // Adjust scale based on your data - .range([chartHeight, 0]); + const scaleY = useMemo( + () => + scale + .scaleLinear() + .domain([Math.min(...data), Math.max(...data)]) // Adjust scale based on your data + .range([chartHeight, 0]), + [data, chartHeight], + ); - const line = shape - .line() - .x( - (_, index) => - halfScreenCompensation + - barWidth / 2 + - index * - ((chartWidth - 2 * halfScreenCompensation) / movingAvgData.length), - ) - .y((d) => scaleY(d))(movingAvgData); + const line = useMemo( + () => + shape + .line() + .x( + (_, index) => + halfScreenCompensation + + barWidth / 2 + + index * + ((chartWidth - 2 * halfScreenCompensation) / + movingAvgData.length), + ) + .y((d) => scaleY(d))(movingAvgData), + [halfScreenCompensation, barWidth, chartWidth, movingAvgData, scaleY], + ); const handleScroll = function (event) { setSelected(selectedBar(event.nativeEvent.contentOffset.x)); }; - const shotAccordionList = useMemo(() => { - return drillData[selected]["shots"].map((shot) => ( - - )); - }, [drillData, drillInfo, selected]); + const shotAccordionList = useMemo( + () => + drillData[selected]["shots"].map((shot) => ( + + )), + [drillData, drillInfo, selected], + ); const styles = StyleSheet.create({ movingAvgContainer: { From 034be6727f347a0ca0fda14c8d5964e818f8c0b4 Mon Sep 17 00:00:00 2001 From: Frankreed Date: Tue, 9 Apr 2024 13:35:13 -0700 Subject: [PATCH 09/11] Paginate barChart --- components/barChart.js | 140 ++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 56 deletions(-) diff --git a/components/barChart.js b/components/barChart.js index e30b0a83..38b58178 100644 --- a/components/barChart.js +++ b/components/barChart.js @@ -1,6 +1,6 @@ import * as scale from "d3-scale"; import * as shape from "d3-shape"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { ScrollView, StyleSheet, @@ -13,17 +13,37 @@ import { Path } from "react-native-svg"; import { BarChart, Grid, YAxis } from "react-native-svg-charts"; import { clampNumber, formatDate, numTrunc } from "~/Utility"; +import { Button } from "react-native-paper"; import ShotAccordion from "~/components/shotAccordion"; export default function BarChartScreen({ drillData, drillInfo }) { if (drillData.length === 0) { return No attempts have been made yet.; } - const data = useMemo(() => { - return drillData - .sort((a, b) => a.time - b.time) - .map((value) => value[drillInfo["mainOutputAttempt"]]); - }, [drillData, drillInfo]); + const scrollViewRef = useRef(); + + const [page, setPage] = useState(0); + + useEffect(() => { + scrollViewRef.current.scrollToEnd({ animated: false }); + }, [page]); + + const sortedDrillData = useMemo( + () => drillData.sort((a, b) => a.time - b.time), + [drillData], + ); + + const itemsPerPage = 300; + + const endIndex = sortedDrillData.length - page * itemsPerPage; + const startIndex = Math.max(endIndex - itemsPerPage, 0); + const totalPages = Math.ceil(sortedDrillData.length / itemsPerPage); + + const slicedDrillData = sortedDrillData.slice(startIndex, endIndex); + + const data = slicedDrillData.map( + (value) => value[drillInfo["mainOutputAttempt"]], + ); const [movingAvgRange, setMovingAvgRange] = useState(5); const [movingAvgRangeValues] = useState([ @@ -52,55 +72,38 @@ export default function BarChartScreen({ drillData, drillInfo }) { ); }; - const processedData = useMemo( - () => - data.map((value, index) => ({ - value: value, - index: index, - svg: { - fill: value > 0 ? "green" : "red", - }, - })), - [data], - ); + const processedData = data.map((value, index) => ({ + value: value, + index: index, + svg: { + fill: value > 0 ? "green" : "red", + }, + })); - const movingAvgData = useMemo( - () => - data.map((value, index) => - index + 1 >= movingAvgRange - ? data - .slice(index - movingAvgRange + 1, index + 1) - .reduce((a, b) => a + b, 0) / movingAvgRange - : 0, - ), - [data, movingAvgRange], + const movingAvgData = data.map((value, index) => + index + 1 >= movingAvgRange + ? data + .slice(index - movingAvgRange + 1, index + 1) + .reduce((a, b) => a + b, 0) / movingAvgRange + : 0, ); // Calculate scales - const scaleY = useMemo( - () => - scale - .scaleLinear() - .domain([Math.min(...data), Math.max(...data)]) // Adjust scale based on your data - .range([chartHeight, 0]), - [data, chartHeight], - ); + const scaleY = scale + .scaleLinear() + .domain([Math.min(...data), Math.max(...data)]) // Adjust scale based on your data + .range([chartHeight, 0]); - const line = useMemo( - () => - shape - .line() - .x( - (_, index) => - halfScreenCompensation + - barWidth / 2 + - index * - ((chartWidth - 2 * halfScreenCompensation) / - movingAvgData.length), - ) - .y((d) => scaleY(d))(movingAvgData), - [halfScreenCompensation, barWidth, chartWidth, movingAvgData, scaleY], - ); + const line = shape + .line() + .x( + (_, index) => + halfScreenCompensation + + barWidth / 2 + + index * + ((chartWidth - 2 * halfScreenCompensation) / movingAvgData.length), + ) + .y((d) => scaleY(d))(movingAvgData); const handleScroll = function (event) { setSelected(selectedBar(event.nativeEvent.contentOffset.x)); @@ -108,15 +111,15 @@ export default function BarChartScreen({ drillData, drillInfo }) { const shotAccordionList = useMemo( () => - drillData[selected]["shots"].map((shot) => ( + sortedDrillData[selected]["shots"].map((shot) => ( )), - [drillData, drillInfo, selected], + [sortedDrillData, drillInfo, selected], ); const styles = StyleSheet.create({ @@ -212,16 +215,40 @@ export default function BarChartScreen({ drillData, drillInfo }) { style={styles.dropdown} /> + + {formatDate(sortedDrillData[startIndex]["time"])} -{" "} + {formatDate(sortedDrillData[endIndex - 1]["time"])} + + + + + `${value}`} // Format label as needed /> item.value} pointerEvents={"none"} + key={page} //force barchart to re-render > - + - {formatDate(drillData[selected]["time"])} + {formatDate(sortedDrillData[selected]["time"])} Date: Tue, 9 Apr 2024 14:01:29 -0700 Subject: [PATCH 10/11] very minor tick change/add back --- components/barChart.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/barChart.js b/components/barChart.js index 38b58178..1bd97bc8 100644 --- a/components/barChart.js +++ b/components/barChart.js @@ -245,6 +245,7 @@ export default function BarChartScreen({ drillData, drillInfo }) { data={data} style={styles.yAxis} formatLabel={(value) => `${value}`} // Format label as needed + numberOfTicks={7} /> item.value} pointerEvents={"none"} key={page} //force barchart to re-render + numberOfTicks={7} > Date: Tue, 9 Apr 2024 14:35:37 -0700 Subject: [PATCH 11/11] barChart now displays 0 as the minimum instead of the lowest value (if the lowest value is lower than 0, display that instead). Illegal attempts shall be terminated --- components/barChart.js | 27 ++++++++++++++++++++++++--- hooks/removeAttempt.js | 8 ++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 hooks/removeAttempt.js diff --git a/components/barChart.js b/components/barChart.js index 1bd97bc8..8c3935d1 100644 --- a/components/barChart.js +++ b/components/barChart.js @@ -15,6 +15,8 @@ import { clampNumber, formatDate, numTrunc } from "~/Utility"; import { Button } from "react-native-paper"; import ShotAccordion from "~/components/shotAccordion"; +import { currentAuthContext } from "../context/Auth"; +import { removeAttempt } from "../hooks/removeAttempt"; export default function BarChartScreen({ drillData, drillInfo }) { if (drillData.length === 0) { @@ -24,6 +26,8 @@ export default function BarChartScreen({ drillData, drillInfo }) { const [page, setPage] = useState(0); + const { currentTeamId } = currentAuthContext(); + useEffect(() => { scrollViewRef.current.scrollToEnd({ animated: false }); }, [page]); @@ -41,9 +45,22 @@ export default function BarChartScreen({ drillData, drillInfo }) { const slicedDrillData = sortedDrillData.slice(startIndex, endIndex); - const data = slicedDrillData.map( - (value) => value[drillInfo["mainOutputAttempt"]], - ); + const data = slicedDrillData.map((value) => { + if (isNaN(value[drillInfo["mainOutputAttempt"]])) { + //the terminator + removeAttempt({ currentTeamId, attemptId: value["id"] }).then(() => { + console.log( + "terminated attempt: ", + value["id"], + " due to illegal value", + ); + }); + return 0; + } + return value[drillInfo["mainOutputAttempt"]]; + }); + + console.log("data: ", data); const [movingAvgRange, setMovingAvgRange] = useState(5); const [movingAvgRangeValues] = useState([ @@ -246,6 +263,8 @@ export default function BarChartScreen({ drillData, drillInfo }) { style={styles.yAxis} formatLabel={(value) => `${value}`} // Format label as needed numberOfTicks={7} + min={Math.min(...data, 0)} + contentInset={{ bottom: 5 }} /> item.value} pointerEvents={"none"} key={page} //force barchart to re-render numberOfTicks={7} + yMin={Math.min(...data, 0)} > { + if (attemptId) { + const attemptDoc = doc(db, "teams", currentTeamId, "attempts", attemptId); + await deleteDoc(attemptDoc); + } +};