Harry de Boer 6 лет назад
Родитель
Сommit
e123034359

+ 1 - 9
src/main/java/puzzeltocht/controller/dto/TeamUpdateDto.java

@@ -2,23 +2,15 @@ package puzzeltocht.controller.dto;
 
 import puzzeltocht.domain.Location;
 
-import java.util.UUID;
-
 public class TeamUpdateDto {
-    private final UUID missionId;
     private final Location location;
     private final String answer;
 
-    public TeamUpdateDto(UUID missionId, Location location, String answer) {
-        this.missionId = missionId;
+    public TeamUpdateDto(Location location, String answer) {
         this.location = location;
         this.answer = answer;
     }
 
-    public UUID getMissionId() {
-        return missionId;
-    }
-
     public Location getLocation() {
         return location;
     }

+ 1 - 1
src/main/resources/static/index.html

@@ -22,7 +22,7 @@
     </p>
     <form id="joinForm">
         <label for="teamName">Teamnaam: </label>
-        <input type="text" id="teamName"/>
+        <input type="text" id="teamName" minlength="3" required/>
         <label for="events">Puzzeltocht:</label>
         <div id="events">
             <label>Laden...</label>

+ 4 - 0
src/main/resources/static/main.css

@@ -69,6 +69,10 @@ body > footer {
     color: #eeeeee;
 }
 
+#joinForm > input:invalid {
+    border: 1px solid #c23350;
+}
+
 #events {
     display: flex;
     flex-direction: column;

+ 165 - 82
src/main/resources/static/main.js

@@ -1,138 +1,221 @@
 "use strict";
 
-let p = undefined;
+let puzzeltocht = undefined;
 window.addEventListener("DOMContentLoaded", () => {
-    p = new Puzzeltocht();
+    puzzeltocht = new Puzzeltocht();
 });
 
 class Puzzeltocht {
 
     constructor() {
-        if ("geolocation" in navigator) {
-            console.log("Geolocation API available");
-        } else {
-            console.log("Geolocation API not available");
-            document.getElementById("unsupported").style.display = "block";
+        this.joinScreen = new JoinScreen();
+        this.location = new LocationApi();
+        console.log("puzzeltocht initialised");
+    }
+
+    startEvent(eventId, teamId) {
+        this.eventId = this.joinScreen.eventId;
+        this.teamId = teamId;
+        document.getElementById("join").style.display = "none";
+        document.getElementById("mission").style.display = "block";
+        document.getElementById("questionMission").style.display = "none";
+        this.location.enable(p => this.updateLocation(p))
+    }
+
+    updateLocation(position) {
+        let l = document.getElementById("location");
+        if (position.accuracy() > 20) {
+            l.innerText = "Onbetrouwbare locatie: " + position.string();
+            return;
         }
 
-        if (window.isSecureContext) {
-            console.log("Secure context available");
-        } else {
-            console.log("Secure context not available");
-            document.getElementById("unsupported").style.display = "block";
+        l.innerText = position.string();
+
+        if (this.lastUpdate === undefined || Date.now() - this.lastUpdate > 5000) {
+            Api.sendLocation(this.eventId, this.teamId, position)
+                .then(m => this.updateMission(m))
+                .catch(e => this.onError(e));
+            this.lastUpdate = Date.now();
         }
+    }
 
-        Puzzeltocht.fetchEvents();
-        document.getElementById("joinForm").addEventListener("submit", (e) => this.joinEvent(e));
-        console.log("puzzeltocht initialised");
+    updateMission(m) {
+        this.mission = m;
+        document.getElementById("missionTitle").innerText = m.title + " (" + m.distanceToTarget + "m)";
+        document.getElementById("missionDescription").innerText = m.description;
     }
 
-    static fetchEvents() {
-        fetch("/api/event", {method: "GET"})
-            .then(r => r.json())
-            .then(j => Puzzeltocht.showEvents(j))
-            .catch(r => console.log(r));
+    onError(e) {
+        this.location.disable();
+        let l = document.getElementById("location");
+        l.innerText = "Error!";
+        console.log(e);
     }
 
-    static showEvents(eventJson) {
-        let events = document.getElementById("events");
-        events.innerHTML = "";
-        eventJson.forEach((e) => {
-            events.innerHTML += '<label><input name="eventId" value="' + e.id + '" type="radio">' + e.title + '</label>';
-        });
-        document.getElementById("joinButton").disabled = false;
+}
+
+class JoinScreen {
+
+    constructor() {
+        Api.fetchEvents()
+            .then(JoinScreen.showEvents)
+            .catch(console.log);
+        document.getElementById("joinForm").addEventListener("submit", (e) => this.joinEvent(e));
     }
 
-    joinEvent(e) {
-        e.preventDefault();
+    joinEvent(submitEvent) {
+        submitEvent.preventDefault();
         let form = document.getElementById("joinForm");
         this.eventId = form.elements["eventId"].value;
-        this.teamNaam = document.getElementById("teamName").value;
+        let teamName = form.elements["teamName"].value;
 
-        if (this.eventId === "" || this.teamNaam === "") {
-            console.log("niet gevuld");
+        if (this.eventId === "" || teamName === "") {
+            console.log("required form field empty");
             return;
         }
 
         document.getElementById("joinButton").disabled = true;
 
-        fetch("/api/event/" + encodeURIComponent(this.eventId) + "/team", {
-                method: "POST",
-                headers: {"Content-Type": "application/json"},
-                body: JSON.stringify(this.teamNaam)
-            })
-            .then(r => r.json())
-            .then(j => p.startEvent(j))
+        Api.join(this.eventId, teamName)
+            .then(teamId => puzzeltocht.startEvent(this.eventId, teamId))
             .catch(r => {
                 console.log(r);
                 document.getElementById("joinButton").disabled = false;
             });
-
-        return false;
     }
 
-    startEvent(teamId) {
-        this.teamId = teamId;
-        this.mission = null;
-        document.getElementById("join").style.display = "none";
-        document.getElementById("mission").style.display = "block";
-        document.getElementById("questionMission").style.display = "none";
-        this.enableLocation()
+    static showEvents(eventJson) {
+        let events = document.getElementById("events");
+        events.innerHTML = "";
+        eventJson.forEach((e) => {
+            events.innerHTML += '<label><input name="eventId" value="' + e.id + '" type="radio">' + e.title + '</label>';
+        });
+        document.getElementById("joinButton").disabled = false;
     }
 
-    updateLocation(pos) {
-        let l = document.getElementById("location");
-        let ts = Date.now() - pos.timestamp;
-        l.innerText = "Locatie (" + Puzzeltocht.lat(pos) + ", " + Puzzeltocht.lon(pos) + ") "
-            + "(+- " + pos.coords.accuracy.toFixed(1) + "m, bijgewerkt " + ts + "s geleden)";
+}
 
-        if (this.lastUpdate === undefined || Date.now() - this.lastUpdate > 5000) {
-            this.sendLocation(pos);
-            this.lastUpdate = Date.now();
-        }
+class Api {
+
+    static fetchEvents() {
+        return FetchJson.get("/api/event")
+    }
+
+    static join(eventId, teamName) {
+        let url = "/api/event/" + encodeURIComponent(eventId) + "/team";
+        return FetchJson.post(url, teamName);
     }
 
-    sendLocation(pos) {
-        let r = {
-            missionId: this.mission == null ? null : this.mission.id,
-            location: {latitude: Puzzeltocht.lat(pos), longitude: Puzzeltocht.lon(pos)},
+    static sendLocation(eventId, teamId, position) {
+        let body = {
+            location: {latitude: position.lat(), longitude: position.lon()},
             answer: null,
         };
-        fetch("/api/event/" + encodeURIComponent(this.eventId) + "/team/" + encodeURIComponent(this.teamId), {
+        let url = "/api/event/" + encodeURIComponent(eventId) + "/team/" + encodeURIComponent(teamId);
+        return FetchJson.put(url, body);
+    }
+
+
+}
+
+class FetchJson {
+
+    static get(url) {
+        return fetch(url, {method: "GET"})
+            .then(FetchJson.responseBody)
+    }
+
+    static post(url, body) {
+        let options = {
+            method: "POST",
+            headers: {"Content-Type": "application/json"},
+            body: JSON.stringify(body)
+        };
+        return fetch(url, options)
+            .then(FetchJson.responseBody)
+    }
+
+    static put(url, body) {
+        let options = {
             method: "PUT",
             headers: {"Content-Type": "application/json"},
-            body: JSON.stringify(r)
-        })
-            .then(r => r.json())
-            .then(j => p.updateMission(j))
-            .catch(r => console.log(r));
+            body: JSON.stringify(body)
+        };
+        return fetch(url, options)
+            .then(FetchJson.responseBody)
     }
 
-    updateMission(m) {
-        this.mission = m;
-        document.getElementById("missionTitle").innerText = m.title + " (" + m.distanceToTarget + "m)";
-        document.getElementById("missionDescription").innerText = m.description;
+    static responseBody(r) {
+        if (!r.ok) {
+            throw Error("HTTP " + r.status + " " + r.statusText + " " + r.url);
+        }
+        return r.json();
+    }
+
+}
+
+class Location {
+
+    constructor(pos) {
+        this.pos = pos;
+    }
+
+    accuracy() {
+        return this.pos.coords.accuracy.toFixed(1)
+    }
+
+    age() {
+        return Date.now() - this.pos.timestamp;
     }
 
-    enableLocation() {
+    lat() {
+        return this.pos.coords.latitude.toFixed(5);
+    }
+
+    lon() {
+        return this.pos.coords.longitude.toFixed(5);
+    }
+
+    string() {
+        return "(" + this.lat() + ", " + this.lon() + ")"
+        + " +-" + this.accuracy() + "m, " + this.age() + "s geleden"
+    }
+}
+
+class LocationApi {
+
+    constructor() {
+        if ("geolocation" in navigator) {
+            console.log("Geolocation API available");
+        } else {
+            console.log("Geolocation API not available");
+            document.getElementById("unsupported").style.display = "block";
+        }
+
+        if (window.isSecureContext) {
+            console.log("Secure context available");
+        } else {
+            console.log("Secure context not available");
+            document.getElementById("unsupported").style.display = "block";
+        }
+    }
+
+    disable() {
+        console.log("disabling geolocation watch " + this.watchId);
+        navigator.geolocation.clearWatch(this.watchId);
+    }
+
+    enable(callback) {
         const geoOpts = {
             enableHighAccuracy: true,
             maximumAge: 10000,
             timeout: 9500
         };
 
-        navigator.geolocation.watchPosition(
-            (pos) => p.updateLocation(pos),
+        this.watchId = navigator.geolocation.watchPosition(
+            (pos) => callback(new Location(pos)),
             (err) => console.log(err),
             geoOpts);
     }
 
-    static lat(pos) {
-        return pos.coords.latitude.toFixed(5);
-    }
-
-    static lon(pos) {
-        return pos.coords.longitude.toFixed(5);
-    }
-
 }