diff --git a/src/App.css b/src/App.css index 88c9891a523a024135a483e54c2cff04b45e3c50..5a477f679bfbf8c30d23e742e4ed96648624cc75 100644 --- a/src/App.css +++ b/src/App.css @@ -300,3 +300,7 @@ div.login button:hover { background-color: rgba(0, 0, 0, 0.5); color: white; } + +.filterContainer input:hover { + cursor: pointer; +} diff --git a/src/components/GameCard.js b/src/components/GameCard.js index 39111e9cd096dbc6549fb806b6235412c8d0d293..18a1e67a7a5287cffa1d189caf2b8d926d171b64 100644 --- a/src/components/GameCard.js +++ b/src/components/GameCard.js @@ -54,10 +54,17 @@ export default class GameCard extends React.Component { <Link to={{ pathname: "/game", search: "?id=" + this.state.gameInfo.id }} > - <button id={"select" + this.state.gameInfo.name} type="button"> + <button id={`select${this.state.gameInfo.name}`} type="button"> Select </button> </Link> + <Link + to={{ pathname: "/replay", search: "?id=" + this.state.gameInfo.id }} + > + <button id={`replay${this.state.gameInfo.name}`} type="button"> + Replay + </button> + </Link> </div> ); } diff --git a/src/components/ReplayMap.js b/src/components/ReplayMap.js index f173ffd93b7bccb67fd61e031800ccb6ac857dbe..1e6305e1fe5e32757d3f78f8884f188aa862153a 100644 --- a/src/components/ReplayMap.js +++ b/src/components/ReplayMap.js @@ -1,13 +1,12 @@ // https://github.com/linghuam/Leaflet.TrackPlayBack import React from "react"; +import { Link } from "react-router-dom"; import L from "leaflet"; -import { Map, TileLayer, ZoomControl, Marker, Popup } from "react-leaflet"; import "../track-playback/src/leaflet.trackplayback/clock"; import "../track-playback/src/leaflet.trackplayback/index"; import "../track-playback/src/control.trackplayback/control.playback"; import "../track-playback/src/control.trackplayback/index"; -import DrawGeoJSON from "./DrawGeoJSON"; export default class ReplayMap extends React.Component { constructor(props) { @@ -17,81 +16,88 @@ export default class ReplayMap extends React.Component { playback: null, // stores player locations from backend data: null, + // stores all factions from the game + factions: [], // stores all drawings from backend allGeoJSON: [], // stores all active drawings on the map activeGeoJSON: [] }; - this.map = React.createRef(); } async componentDidMount() { - await this.fetchPlayerData(); - //await this.fetchDrawingData(); - //this.tickDrawings(); - this.replay(); + // set gameId to state from URL + await this.setState({ + gameId: await new URL(window.location.href).searchParams.get("id") + }); + // fetch player data with gameId + // throws error if game is not found and redirects back to game selection + await this.setState({ + data: await this.fetchPlayerData() + }); + // fetch factions from the game + await this.setState({ + factions: await this.fetchFactions() + }); + // fetch drawings with gameId + await this.setState({ + allGeoJSON: await this.fetchDrawingData() + }); + // WIP, map only active drawings to activeGeoJSON state + await this.setState({ + activeGeoJSON: this.tickDrawings() + }); + // if game was found but game has no replay data, alert the user + this.state.data.length > 1 + ? this.replay() + : alert("No replay data was found"); } - componentWillReceiveProps(nextProps) {} - - // cloud game a1231e2b-aa29-494d-b687-ea2d48cc23df - // local game wimma 314338f9-b0bb-4bf7-9554-769c7b409bce - // local game vbox 16977b13-c419-48b4-b7d6-e7620f27bf39 - // fetch player locations from the game fetchPlayerData = async () => { - await fetch( - `${ - process.env.REACT_APP_API_URL - }/replay/players/314338f9-b0bb-4bf7-9554-769c7b409bce`, - { - method: "GET" - } - ) - .then(async res => await res.json()) - .then( - async res => { - await this.setState({ data: res }); - }, - // Note: it's important to handle errors here - // instead of a catch() block so that we don't swallow - // exceptions from actual bugs in components. - error => { - console.log(error); - } - ); + let res = await fetch( + `${process.env.REACT_APP_API_URL}/replay/players/${this.state.gameId}` + ); + if (await res.ok) { + return await res.json(); + } else { + alert("Game not found"); + window.document.location.href = "/"; + } + }; + + fetchFactions = async () => { + let res = await fetch( + `${process.env.REACT_APP_API_URL}/game/${this.state.gameId}` + ); + if (await res.ok) { + let data = await res.json(); + let factions = data.factions.map(faction => { + return { + name: faction.factionName, + colour: faction.colour, + active: true + }; + }); + return await factions; + } else { + alert(res.statusText); + } }; fetchDrawingData = async () => { - await fetch( - `${process.env.REACT_APP_API_URL}/replay/{ - "lng": 25.72588, - "lat": 62.23147 -}`, - { - method: "GET" - } - ) - .then(async res => await res.json()) - .then( - async res => { - await this.setState({ allGeoJSON: res }); - }, - error => { - console.log(error); - } - ); + let res = await fetch( + `${process.env.REACT_APP_API_URL}/replay/${this.state.gameId}` + ); + if (await res.ok) { + return await res.json(); + } else { + alert(res.statusText); + } }; tickDrawings = () => { - let activeDrawings = []; - this.state.allGeoJSON.map(drawing => { - activeDrawings.push(drawing[0]); - this.setState({ - activeGeoJSON: { - type: "FeatureCollection", - features: [...activeDrawings] - } - }); + return this.state.allGeoJSON.map(drawing => { + return drawing[0]; }); }; @@ -140,6 +146,9 @@ export default class ReplayMap extends React.Component { offset: [0, 0], direction: "top", permanent: false + }, + filterOptions: { + factions: this.state.factions } }); this.setState({ @@ -170,6 +179,9 @@ export default class ReplayMap extends React.Component { )} </Map> */ <React.Fragment> + <Link to="/"> + <button>Game selection</button> + </Link> <div className="map" ref="map" /> </React.Fragment> ); diff --git a/src/track-playback/src/control.trackplayback/control.playback.js b/src/track-playback/src/control.trackplayback/control.playback.js index 6c6f101980579abf0abab21b9d32c2d4626abca4..18ee33f079993f32cc1b92a2d197f82845cb0776 100644 --- a/src/track-playback/src/control.trackplayback/control.playback.js +++ b/src/track-playback/src/control.trackplayback/control.playback.js @@ -100,12 +100,55 @@ export const TrackPlayBackControl = L.Control.extend({ "sliderContainer", this._container ); - this._lineCbx = this._createCheckbox( + this._filterContainer = this._createContainer( + "filterContainer", + this._container + ); + /* this._lineCbx = this._createCheckbox( "show trackLine", "show-trackLine", this._optionsContainer, this._showTrackLine + ); */ + // create checkboxes for filtering persons based on their class + this._filterInfantry = this._createCheckbox( + "show infantry units", + "show-infantry", + this._filterContainer, + this._showInfantry + ); + this._filterRecon = this._createCheckbox( + "show recon units", + "show-recon", + this._filterContainer, + this._showRecon ); + this._filterMechanized = this._createCheckbox( + "show mechanized units", + "show-mechanized", + this._filterContainer, + this._showMechanized + ); + // show some text between class based and faction based filtering + this._factionText = this._createInfo( + "Faction filtering:", + "", + "faction-text-filter", + this._filterContainer + ); + // create checkboxes for filtering persons based on their faction + let factions = this.trackPlayBack.passFactions(); + let factionCheckboxes = []; + factions.map(faction => { + factionCheckboxes.push( + this._createCheckbox( + `show ${faction.name}`, + `show-${faction.name}`, + this._filterContainer, + this._showFaction + ) + ); + }); this._playBtn = this._createButton( "play", @@ -120,13 +163,13 @@ export const TrackPlayBackControl = L.Control.extend({ this._restart ); this._slowSpeedBtn = this._createButton( - "slow", + "decrease speed", "btn-slow", this._buttonContainer, this._slow ); this._quickSpeedBtn = this._createButton( - "quick", + "increase speed", "btn-quick", this._buttonContainer, this._quick @@ -187,6 +230,7 @@ export const TrackPlayBackControl = L.Control.extend({ let inputId = `trackplayback-input-${L.Util.stamp(inputEle)}`; inputEle.setAttribute("type", "checkbox"); inputEle.setAttribute("id", inputId); + inputEle.checked = true; let labelEle = L.DomUtil.create("label", "trackplayback-label", divEle); labelEle.setAttribute("for", inputId); @@ -250,6 +294,27 @@ export const TrackPlayBackControl = L.Control.extend({ } }, + _showInfantry(e) { + this.trackPlayBack.toggleInfantry(e.target.checked); + }, + + _showRecon(e) { + this.trackPlayBack.toggleRecon(e.target.checked); + }, + + _showMechanized(e) { + this.trackPlayBack.toggleMechanized(e.target.checked); + }, + _showFaction(e) { + this.trackPlayBack.toggleFactions( + e.target.checked, + e.target.parentNode.className.substring( + 5, + e.target.parentNode.className.indexOf(" ") + ) + ); + }, + _play: function() { let hasClass = L.DomUtil.hasClass(this._playBtn, "btn-stop"); if (hasClass) { diff --git a/src/track-playback/src/leaflet.trackplayback/draw.js b/src/track-playback/src/leaflet.trackplayback/draw.js index ba05a447d31c1f556076c475519b2401a460d501..9949f45fd090be1efdf684b9fcc556ee0eb66ac6 100644 --- a/src/track-playback/src/leaflet.trackplayback/draw.js +++ b/src/track-playback/src/leaflet.trackplayback/draw.js @@ -40,12 +40,19 @@ export const Draw = L.Class.extend({ direction: "top", permanent: false }, + filterOptions: { + infantry: true, + recon: true, + mechanized: true, + factions: [] + }, initialize: function(map, options) { L.extend(this.trackPointOptions, options.trackPointOptions); L.extend(this.trackLineOptions, options.trackLineOptions); L.extend(this.targetOptions, options.targetOptions); L.extend(this.toolTipOptions, options.toolTipOptions); + L.extend(this.filterOptions, options.filterOptions); this._showTrackPoint = this.trackPointOptions.isDraw; this._showTrackLine = this.trackLineOptions.isDraw; @@ -189,12 +196,26 @@ export const Draw = L.Class.extend({ } // 画船 let targetPoint = trackpoints[trackpoints.length - 1]; + // get info from first trackpoint let info = trackpoints[0].info; - if (this.targetOptions.useImg && this._targetImg) { + let skip = false; + // check if faction has been filtered and skip drawing if it is + this.filterOptions.factions.forEach(faction => { + if ( + !faction.active && + trackpoints[0].info[1]["value"] === faction.colour + ) { + skip = true; + } + }); + + // compare icon to filter, draw if true else skip + if (!skip && this.filterOptions[info[0]["value"].slice(0, -4)]) { this._drawShipImage(targetPoint, info); - } else { - this._drawShipCanvas(targetPoint); } + /* else { + this._drawShipCanvas(targetPoint); + } */ // ç”»æ ‡æ³¨ä¿¡æ¯ if (this.targetOptions.showText) { this._drawtxt(`航å‘:${parseInt(targetPoint.dir)}度`, targetPoint); diff --git a/src/track-playback/src/leaflet.trackplayback/trackplayback.js b/src/track-playback/src/leaflet.trackplayback/trackplayback.js index 460102ff1b594c01fad7396cb9517366a04242f8..5aa35aa0cabe74f17ee026b3108bc55bacad4b45 100644 --- a/src/track-playback/src/leaflet.trackplayback/trackplayback.js +++ b/src/track-playback/src/leaflet.trackplayback/trackplayback.js @@ -1,18 +1,10 @@ -import L from 'leaflet' +import L from "leaflet"; -import { - Track -} from './track' -import { - TrackController -} from './trackcontroller' -import { - Clock -} from './clock' -import { - Draw -} from './draw' -import * as Util from './util' +import { Track } from "./track"; +import { TrackController } from "./trackcontroller"; +import { Clock } from "./clock"; +import { Draw } from "./draw"; +import * as Util from "./util"; /** * single track data @@ -22,110 +14,138 @@ import * as Util from './util' * [single track data, single track data, single track data] */ export const TrackPlayBack = L.Class.extend({ - includes: L.Mixin.Events, - initialize: function (data, map, options = {}) { + initialize: function(data, map, options = {}) { let drawOptions = { trackPointOptions: options.trackPointOptions, trackLineOptions: options.trackLineOptions, targetOptions: options.targetOptions, - toolTipOptions: options.toolTipOptions - } - this.tracks = this._initTracks(data) - this.draw = new Draw(map, drawOptions) - this.trackController = new TrackController(this.tracks, this.draw) - this.clock = new Clock(this.trackController, options.clockOptions) + toolTipOptions: options.toolTipOptions, + filterOptions: options.filterOptions + }; + this.tracks = this._initTracks(data); + this.draw = new Draw(map, drawOptions); + this.trackController = new TrackController(this.tracks, this.draw); + this.clock = new Clock(this.trackController, options.clockOptions); - this.clock.on('tick', this._tick, this) + this.clock.on("tick", this._tick, this); + }, + start: function() { + this.clock.start(); + return this; + }, + stop: function() { + this.clock.stop(); + return this; + }, + rePlaying: function() { + this.clock.rePlaying(); + return this; }, - start: function () { - this.clock.start() - return this + slowSpeed: function() { + this.clock.slowSpeed(); + return this; }, - stop: function () { - this.clock.stop() - return this + quickSpeed: function() { + this.clock.quickSpeed(); + return this; }, - rePlaying: function () { - this.clock.rePlaying() - return this + getSpeed: function() { + return this.clock.getSpeed(); }, - slowSpeed: function () { - this.clock.slowSpeed() - return this + getCurTime: function() { + return this.clock.getCurTime(); }, - quickSpeed: function () { - this.clock.quickSpeed() - return this + getStartTime: function() { + return this.clock.getStartTime(); }, - getSpeed: function () { - return this.clock.getSpeed() + getEndTime: function() { + return this.clock.getEndTime(); }, - getCurTime: function () { - return this.clock.getCurTime() + isPlaying: function() { + return this.clock.isPlaying(); }, - getStartTime: function () { - return this.clock.getStartTime() + setCursor: function(time) { + this.clock.setCursor(time); + return this; }, - getEndTime: function () { - return this.clock.getEndTime() + setSpeed: function(speed) { + this.clock.setSpeed(speed); + return this; }, - isPlaying: function () { - return this.clock.isPlaying() + showTrackPoint: function() { + this.draw.showTrackPoint(); + return this; }, - setCursor: function (time) { - this.clock.setCursor(time) - return this + hideTrackPoint: function() { + this.draw.hideTrackPoint(); + return this; }, - setSpeed: function (speed) { - this.clock.setSpeed(speed) - return this + showTrackLine: function() { + this.draw.showTrackLine(); + return this; }, - showTrackPoint: function () { - this.draw.showTrackPoint() - return this + hideTrackLine: function() { + this.draw.hideTrackLine(); + return this; }, - hideTrackPoint: function () { - this.draw.hideTrackPoint() - return this + // toggles the visibility of infantry units on the map + toggleInfantry: function(e) { + this.draw.filterOptions.infantry = e; + return this; }, - showTrackLine: function () { - this.draw.showTrackLine() - return this + // toggles the visibility of recon units on the map + toggleRecon: function(e) { + this.draw.filterOptions.recon = e; + return this; + }, + // toggles the visibility of mechanized units on the map + toggleMechanized: function(e) { + this.draw.filterOptions.mechanized = e; + return this; + }, + // toggles the visibility of faction units on the map + toggleFactions: function(e, target) { + for (let faction of this.draw.filterOptions.factions) { + if (faction.name === target) { + faction.active = e; + break; + } + } }, - hideTrackLine: function () { - this.draw.hideTrackLine() - return this + // pass the factions to control playback to show faction names on the control panel + passFactions: function() { + return this.draw.filterOptions.factions; }, - dispose: function () { - this.clock.off('tick', this._tick) - this.draw.remove() - this.tracks = null - this.draw = null - this.trackController = null - this.clock = null + dispose: function() { + this.clock.off("tick", this._tick); + this.draw.remove(); + this.tracks = null; + this.draw = null; + this.trackController = null; + this.clock = null; }, - _tick: function (e) { - this.fire('tick', e) + _tick: function(e) { + this.fire("tick", e); }, - _initTracks: function (data) { - let tracks = [] + _initTracks: function(data) { + let tracks = []; if (Util.isArray(data)) { if (Util.isArray(data[0])) { // 多æ¡è½¨è¿¹ for (let i = 0, len = data.length; i < len; i++) { - tracks.push(new Track(data[i])) + tracks.push(new Track(data[i])); } } else { // å•æ¡è½¨è¿¹ - tracks.push(new Track(data)) + tracks.push(new Track(data)); } } - return tracks + return tracks; } -}) +}); -export const trackplayback = function (data, map, options) { - return new TrackPlayBack(data, map, options) -} +export const trackplayback = function(data, map, options) { + return new TrackPlayBack(data, map, options); +};