Selaa lähdekoodia

Splitting RoundSummary into a directory, and adding the top scorer live update feature

Kirk Trombley 5 vuotta sitten
vanhempi
commit
f7bff07568

+ 1 - 0
client/src/components/Game/Game.jsx

@@ -51,6 +51,7 @@ const Game = () => {
       />
     case POST_ROUND:
       return <RoundSummary
+        gameId={state.gameId}
         round={state.lastRound}
         onNext={() => setGameState(IN_ROUND)}
       />

+ 0 - 110
client/src/components/screens/RoundSummary.jsx

@@ -1,110 +0,0 @@
-import React, { useRef, useEffect } from "react";
-import ClickToCopy from "../util/ClickToCopy";
-import Button from "../util/Button";
-import useMap from "../../hooks/useMap";
-import flag from "../../assets/checkered-flag.svg";
-import question from "../../assets/question-mark.svg";
-/* global google */
-
-// TODO eventually we want this to query and show other players
-
-const questionIcon = {
-  url: question,
-  scaledSize: new google.maps.Size(64, 64),
-  origin: new google.maps.Point(0, 0),
-  anchor: new google.maps.Point(32, 48),
-};
-
-const flagIcon = {
-  url: flag,
-  scaledSize: new google.maps.Size(32, 32),
-  origin: new google.maps.Point(0, 0),
-  anchor: new google.maps.Point(4, 32),
-};
-
-const lineSettings = {
-  strokeColor: "#333333",
-  strokeOpacity: 0,
-  icons: [{
-    icon: {
-      path: 'M 0,-1 0,1',
-      strokeOpacity: 1,
-      scale: 4
-    },
-    offset: '0',
-    repeat: '20px'
-  }],
-};
-
-const openMapInNewTab = ({ lat, lng }) => {
-  window.open(`https://www.google.com/maps?hl=en&q=+${lat},+${lng}`, "_blank");
-}
-
-const makeMarker = (map, position, title, icon) => {
-  const marker = new google.maps.Marker({ 
-    clickable: true,
-    map,
-    position,
-    title,
-    icon,
-  });
-
-  marker.addListener("click", () => openMapInNewTab(position));
-
-  return marker;
-}
-
-const useMarkedPoints = (mapRef, selectedPoint, targetPoint) => {
-  useEffect(() => {
-    mapRef.current.panTo(targetPoint);
-    mapRef.current.setZoom(4);
-
-    const selectedMarker = makeMarker(mapRef.current, selectedPoint, "Selected", questionIcon);
-    const targetMarker = makeMarker(mapRef.current, targetPoint, "Goal", flagIcon);
-
-    const line = new google.maps.Polyline({
-      path: [ selectedPoint, targetPoint ],
-      map: mapRef.current,
-      ...lineSettings,
-    });
-
-    return () => { 
-      line.setMap(null); 
-      targetMarker.setMap(null);
-      selectedMarker.setMap(null);
-    }
-  }, [mapRef, selectedPoint, targetPoint]);
-}
-
-export default ({ round, onNext }) => {
-  const {
-    roundNum,
-    selectedPoint,
-    targetPoint,
-    score,
-    totalScore,
-  } = round;
-  const mapDivRef = useRef(null);
-  const mapRef = useMap(mapDivRef);
-  useMarkedPoints(mapRef, selectedPoint, targetPoint);
-
-  return (
-    <div className="round-summary">
-      <div className="round-summary__map"><div className="map-div" ref={mapDivRef} /></div>
-      <div className="round-summary__info-pane">
-        {selectedPoint 
-          ? <span className="round-summary__guess">
-              You selected: <ClickToCopy text={`${selectedPoint.lat}, ${selectedPoint.lng}`}/>
-            </span> 
-          : <span className="round-summary__guess round-summary__guess--timed-out">
-              Timed out!
-            </span>
-        }
-        <span className="round-summary__target-point">Target: <ClickToCopy text={`${targetPoint.lat}, ${targetPoint.lng}`}/></span>
-        <span className="round-summary__round-score">Score For Round {roundNum}: {score}</span>
-        <span className="round-summary__total-score">Running Total Score: {totalScore}</span>
-        <Button className="round-summary__button" onClick={onNext}>{roundNum === "5" ? "View Summary" : "Next Round"}</Button>
-      </div>
-    </div>
-  );
-};

+ 69 - 0
client/src/components/screens/RoundSummary/RoundSummary.jsx

@@ -0,0 +1,69 @@
+import React, { useRef } from "react";
+import ClickToCopy from "../../util/ClickToCopy";
+import Button from "../../util/Button";
+import useMap from "../../../hooks/useMap";
+import usePlayerScores from "../../../hooks/usePlayerScores";
+import useMarkedPoints from "./useMarkedPoints";
+
+// TODO eventually we want this to query and show other players
+
+const useTopScorer = (gameId, roundNum) => {
+  const scores = usePlayerScores(gameId);
+
+  if (scores === null) {
+    return { topPlayer: null, topScore: null }
+  }
+
+  const ordered = scores
+    .filter(({ guesses }) => guesses[roundNum] && guesses[roundNum].score)
+    .map(({ name, guesses }) => ({ name, score: guesses[roundNum].score }))
+    .sort((p1, p2) => p1.score > p2.score ? -1 : (p1.score < p2.score ? 1 : 0));
+
+  if (ordered.length === 0) {
+    return { topPlayer: null, topScore: null }
+  }
+
+  return { topPlayer: ordered[0].name, topScore: ordered[0].score };
+}
+
+const InfoPane = ({ gameId, selectedPoint, targetPoint, roundNum, score, totalScore, onNext }) => {
+  const { topPlayer, topScore } = useTopScorer(gameId, roundNum);
+
+  return (
+    <div className="round-summary__info-pane">
+      {selectedPoint 
+        ? <span className="round-summary__guess">
+            You selected: <ClickToCopy text={`${selectedPoint.lat}, ${selectedPoint.lng}`}/>
+          </span> 
+        : <span className="round-summary__guess round-summary__guess--timed-out">
+            Timed out!
+          </span>
+      }
+      <span className="round-summary__target-point">Target: <ClickToCopy text={`${targetPoint.lat}, ${targetPoint.lng}`}/></span>
+      <span className="round-summary__round-score">Score For Round {roundNum}: {score}</span>
+      <span className="round-summary__total-score">Running Total Score: {totalScore}</span>
+      {topPlayer ? <span className="round-summary__top-score">Current best is {topPlayer} with {topScore}</span> : null}
+      <Button className="round-summary__button" onClick={onNext}>{roundNum === "5" ? "View Summary" : "Next Round"}</Button>
+    </div>
+  );
+}
+
+export default ({ gameId, round, onNext }) => {
+  const {
+    roundNum,
+    selectedPoint,
+    targetPoint,
+    score,
+    totalScore,
+  } = round;
+  const mapDivRef = useRef(null);
+  const mapRef = useMap(mapDivRef);
+  useMarkedPoints(mapRef, selectedPoint, targetPoint);
+
+  return (
+    <div className="round-summary">
+      <div className="round-summary__map"><div className="map-div" ref={mapDivRef} /></div>
+      <InfoPane {...{ gameId, selectedPoint, targetPoint, roundNum, score, totalScore, onNext }}/>
+    </div>
+  );
+};

+ 1 - 0
client/src/components/screens/RoundSummary/index.js

@@ -0,0 +1 @@
+export { default } from './RoundSummary';

+ 72 - 0
client/src/components/screens/RoundSummary/useMarkedPoints.jsx

@@ -0,0 +1,72 @@
+import { useEffect } from "react";
+import flag from "../../../assets/checkered-flag.svg";
+import question from "../../../assets/question-mark.svg";
+/* global google */
+
+const questionIcon = {
+  url: question,
+  scaledSize: new google.maps.Size(64, 64),
+  origin: new google.maps.Point(0, 0),
+  anchor: new google.maps.Point(32, 48),
+};
+
+const flagIcon = {
+  url: flag,
+  scaledSize: new google.maps.Size(32, 32),
+  origin: new google.maps.Point(0, 0),
+  anchor: new google.maps.Point(4, 32),
+};
+
+const lineSettings = {
+  strokeColor: "#333333",
+  strokeOpacity: 0,
+  icons: [{
+    icon: {
+      path: 'M 0,-1 0,1',
+      strokeOpacity: 1,
+      scale: 4
+    },
+    offset: '0',
+    repeat: '20px'
+  }],
+};
+
+const openMapInNewTab = ({ lat, lng }) => {
+  window.open(`https://www.google.com/maps?hl=en&q=+${lat},+${lng}`, "_blank");
+}
+
+const makeMarker = (map, position, title, icon) => {
+  const marker = new google.maps.Marker({ 
+    clickable: true,
+    map,
+    position,
+    title,
+    icon,
+  });
+
+  marker.addListener("click", () => openMapInNewTab(position));
+
+  return marker;
+}
+
+export default (mapRef, selectedPoint, targetPoint) => {
+  useEffect(() => {
+    mapRef.current.panTo(targetPoint);
+    mapRef.current.setZoom(4);
+
+    const selectedMarker = makeMarker(mapRef.current, selectedPoint, "Selected", questionIcon);
+    const targetMarker = makeMarker(mapRef.current, targetPoint, "Goal", flagIcon);
+
+    const line = new google.maps.Polyline({
+      path: [ selectedPoint, targetPoint ],
+      map: mapRef.current,
+      ...lineSettings,
+    });
+
+    return () => { 
+      line.setMap(null); 
+      targetMarker.setMap(null);
+      selectedMarker.setMap(null);
+    }
+  }, [mapRef, selectedPoint, targetPoint]);
+}