|
@@ -1,9 +1,11 @@
|
|
-import React, { useRef } from "react";
|
|
|
|
|
|
+import React, { useRef, useEffect } from "react";
|
|
import styled from "styled-components";
|
|
import styled from "styled-components";
|
|
import useMap from "../../../hooks/useMap";
|
|
import useMap from "../../../hooks/useMap";
|
|
-import useMarkedPoints from "./useMarkedPoints";
|
|
|
|
-import RoundInfoPane from "./RoundInfoPane";
|
|
|
|
import { useLastRound } from "../../../domain/gameStore";
|
|
import { useLastRound } from "../../../domain/gameStore";
|
|
|
|
+import flag from "../../../assets/checkered-flag.svg";
|
|
|
|
+import usePlayerScores from "../../../hooks/usePlayerScores";
|
|
|
|
+/* global google */
|
|
|
|
+
|
|
|
|
|
|
const Container = styled.div`
|
|
const Container = styled.div`
|
|
flex: 1;
|
|
flex: 1;
|
|
@@ -26,20 +28,125 @@ const MapDiv = styled.div`
|
|
}
|
|
}
|
|
`
|
|
`
|
|
|
|
|
|
|
|
+const flagIcon = {
|
|
|
|
+ url: flag,
|
|
|
|
+ scaledSize: new google.maps.Size(32, 32),
|
|
|
|
+ origin: new google.maps.Point(0, 0),
|
|
|
|
+ anchor: new google.maps.Point(4, 36),
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const questionSymbol = color => ({
|
|
|
|
+ path: "M29.898 26.5722l-4.3921 0c-0.0118,-0.635 -0.0177,-1.0172 -0.0177,-1.1583 0,-1.4229 0.2352,-2.5929 0.7056,-3.5102 0.4704,-0.9231 1.417,-1.952 2.8281,-3.1044 1.4111,-1.1465 2.2578,-1.8991 2.5282,-2.2578 0.4292,-0.5585 0.6409,-1.1818 0.6409,-1.8579 0,-0.9408 -0.3763,-1.7463 -1.1289,-2.4224 -0.7526,-0.6703 -1.7639,-1.0054 -3.0397,-1.0054 -1.2289,0 -2.2578,0.3527 -3.0868,1.0524 -0.8232,0.6997 -1.3935,1.7698 -1.7051,3.2044l-4.4391 -0.5527c0.1234,-2.0578 0.9995,-3.8041 2.6223,-5.2387 1.6286,-1.4346 3.757,-2.152 6.4029,-2.152 2.7752,0 4.9859,0.7291 6.6322,2.1814 1.6404,1.4522 2.4635,3.1397 2.4635,5.0741 0,1.0642 -0.3057,2.0755 -0.9054,3.028 -0.6056,0.9525 -1.8933,2.2519 -3.8688,3.8923 -1.0231,0.8525 -1.6581,1.5346 -1.905,2.052 -0.2469,0.5174 -0.3587,1.4405 -0.3351,2.7752zm-4.3921 6.5087l0 -4.8389 4.8389 0 0 4.8389 -4.8389 0z",
|
|
|
|
+ fillOpacity: 1.0,
|
|
|
|
+ fillColor: color,
|
|
|
|
+ scale: 1,
|
|
|
|
+ anchor: new google.maps.Point(24, 36),
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const lineSettings = color => ({
|
|
|
|
+ strokeColor: color,
|
|
|
|
+ strokeOpacity: 0,
|
|
|
|
+ icons: [{
|
|
|
|
+ icon: {
|
|
|
|
+ path: 'M 0,-1 0,1',
|
|
|
|
+ strokeOpacity: 1,
|
|
|
|
+ scale: 4
|
|
|
|
+ },
|
|
|
|
+ offset: '0',
|
|
|
|
+ repeat: '20px'
|
|
|
|
+ }],
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const makeMarker = (map, position, title, icon) => {
|
|
|
|
+ const marker = new google.maps.Marker({
|
|
|
|
+ clickable: true,
|
|
|
|
+ map,
|
|
|
|
+ position,
|
|
|
|
+ title,
|
|
|
|
+ icon,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const { lat, lng } = position;
|
|
|
|
+ marker.addListener("click", () => {
|
|
|
|
+ window.open(`https://www.google.com/maps?hl=en&q=+${lat},+${lng}`, "_blank");
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return marker;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const goldenRatioConj = 0.618033988749895;
|
|
|
|
+let h = 0;
|
|
|
|
+const nextColor = () => {
|
|
|
|
+ h += goldenRatioConj;
|
|
|
|
+ h %= 1;
|
|
|
|
+ const s = 0.5;
|
|
|
|
+ const v = 0.9;
|
|
|
|
+ const h_i = Math.floor(h*6)
|
|
|
|
+ const f = h*6 - h_i
|
|
|
|
+ const p = v * (1 - s)
|
|
|
|
+ const q = v * (1 - f*s)
|
|
|
|
+ const t = v * (1 - (1 - f) * s)
|
|
|
|
+ return "#" + ([
|
|
|
|
+ [v, t, p],
|
|
|
|
+ [q, v, p],
|
|
|
|
+ [p, v, t],
|
|
|
|
+ [p, q, v],
|
|
|
|
+ [t, p, v],
|
|
|
|
+ [v, p, q],
|
|
|
|
+ ])[h_i].map(component => {
|
|
|
|
+ const converted = Math.floor(component * 256).toString(16);
|
|
|
|
+ if (converted.length === 1) {
|
|
|
|
+ return "0" + converted;
|
|
|
|
+ }
|
|
|
|
+ return converted;
|
|
|
|
+ }).reduce((x, y) => x + y);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const playerColors = {};
|
|
|
|
+
|
|
export default () => {
|
|
export default () => {
|
|
- const {
|
|
|
|
- selectedPoint,
|
|
|
|
- targetPoint,
|
|
|
|
- } = useLastRound();
|
|
|
|
- const mapDivRef = useRef(null);
|
|
|
|
|
|
+ // get the info about the last round
|
|
|
|
+ const { roundNum, targetPoint } = useLastRound();
|
|
|
|
+
|
|
|
|
+ // draw the map
|
|
// TODO dynamically determine this zoom level?
|
|
// TODO dynamically determine this zoom level?
|
|
|
|
+ const mapDivRef = useRef(null);
|
|
const mapRef = useMap(mapDivRef, targetPoint.lat, targetPoint.lng, 4);
|
|
const mapRef = useMap(mapDivRef, targetPoint.lat, targetPoint.lng, 4);
|
|
- useMarkedPoints(mapRef, selectedPoint, targetPoint);
|
|
|
|
|
|
|
|
|
|
+ // set up the flag at the target point
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ const targetMarker = makeMarker(mapRef.current, targetPoint, "Goal", flagIcon);
|
|
|
|
+ return () => targetMarker.setMap(null);
|
|
|
|
+ }, [mapRef, targetPoint]);
|
|
|
|
+
|
|
|
|
+ // live update the player scores
|
|
|
|
+ const players = usePlayerScores()
|
|
|
|
+ ?.filter(({ guesses }) => guesses[roundNum] && guesses[roundNum].score);
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (!players) return;
|
|
|
|
+ const drawings = [];
|
|
|
|
+ players.forEach(({ name, guesses }) => {
|
|
|
|
+ const { lat, lng, score } = guesses[roundNum]
|
|
|
|
+ const color = playerColors[name] ?? nextColor();
|
|
|
|
+ playerColors[name] = color;
|
|
|
|
+ const selectedPoint = { lat, lng };
|
|
|
|
+
|
|
|
|
+ const marker = makeMarker(mapRef.current, selectedPoint, `${name} - ${score} Points`, questionSymbol(color))
|
|
|
|
+ drawings.push(marker);
|
|
|
|
+
|
|
|
|
+ const line = new google.maps.Polyline({
|
|
|
|
+ path: [ selectedPoint, targetPoint ],
|
|
|
|
+ map: mapRef.current,
|
|
|
|
+ ...lineSettings(color),
|
|
|
|
+ });
|
|
|
|
+ drawings.push(line);
|
|
|
|
+ });
|
|
|
|
+ return () => drawings.forEach((drawing) => drawing.setMap(null));
|
|
|
|
+ }, [players, mapRef, targetPoint, roundNum]);
|
|
|
|
+
|
|
return (
|
|
return (
|
|
<Container>
|
|
<Container>
|
|
<MapDiv ref={mapDivRef} />
|
|
<MapDiv ref={mapDivRef} />
|
|
- <RoundInfoPane/>
|
|
|
|
</Container>
|
|
</Container>
|
|
);
|
|
);
|
|
};
|
|
};
|