layer.on({ click: this.handleFeatureClick(feature) })}\n />\n );\n });\n\n this.setState({ geoJsonElements: geoJsonElements });\n }\n\n let selectedFeature = this.props.selectedFeature;\n\n if (prevProps.selectedFeature !== selectedFeature) {\n let selectedGeoJsonElement = null;\n\n if (selectedFeature) {\n selectedGeoJsonElement = (\n \n );\n }\n\n this.setState({ selectedGeoJsonElement: selectedGeoJsonElement });\n }\n };\n\n flyToPos = (pos, zoom = 16) => {\n let leafletElement = this.leafletMap.current.leafletElement;\n\n leafletElement.flyTo(pos, zoom, { animate: true });\n };\n\n onLeafletViewportChange = () => {\n const center = this.leafletMap.current.leafletElement.getCenter(); //get map center\n this.setState({ center });\n };\n onLeafletViewportChanged = () => {\n if (this.setNewViewportTimer) {\n clearTimeout(this.setNewViewportTimer);\n }\n\n let cb = () => {\n const center = this.leafletMap.current.leafletElement.getCenter(); //get map center\n const screenBounds = this.leafletMap.current.leafletElement.getBounds();\n const bounds = {\n xMin: screenBounds.getWest(),\n xMax: screenBounds.getEast(),\n yMin: screenBounds.getSouth(),\n yMax: screenBounds.getNorth(),\n };\n\n if (this.state.firsttime) {\n this.setState({ firsttime: false }, () => {\n this.props.onMapBoundsChange(bounds, center, true);\n });\n } else {\n this.props.onMapBoundsChange(bounds, center, false);\n }\n };\n\n this.setNewViewportTimer = setTimeout(cb, 400);\n };\n\n render() {\n const mapCenter = [52.09, 5.1]; // Utrecht\n\n const fullStyle = { height: \"100%\", width: \"100%\" };\n\n const { position } = this.props;\n let positionMarker = null;\n let accuracyCircle = null;\n if (position) {\n positionMarker = (\n \n U bent hier \n \n );\n\n accuracyCircle = (\n \n );\n }\n\n return (\n \n
\n \n \n \n \n {this.state.geoJsonElements}\n {this.state.selectedGeoJsonElement}\n \n \n {true ? accuracyCircle : null}\n {positionMarker}\n {this.props.selectLocation && this.state.center && (\n \n New plot position \n \n )}\n \n \n
\n );\n }\n}\n\nexport default MainMap;\n","import React, { Component } from 'react';\n\nimport Button from '@material-ui/core/Button';\nimport AppBar from '@material-ui/core/AppBar';\nimport Toolbar from '@material-ui/core/Toolbar';\n\nimport './Navbar.css'\n\nclass NavBar extends Component {\n\tconstructor(props) {\n\t\tsuper(props);\n\n\t\tthis.state = {\n\t\t\topen: false\n\t\t};\n\t}\n\n\tcomponentDidMount = () => {\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t this.props.onOpenAccount(true)}>\n\t\t\t\t\t\t{this.props.user ? this.props.user.username : 'Login'}\n\t\t\t\t\t \n\t\t\t\t \n\t\t\t \n\t\t);\n\t}\n}\n\nexport default NavBar;\n","const apiUrl = \"https://api.ellipsis-drive.com/v1\";\n// const apiUrl = \"https://dev.api.ellipsis-drive.com/v1\";\n\n// const apiUrl = 'http://localhost:7553/v1';\n\nconst ApiManager = {\n adminUserName: \"admin\",\n\n apiUrl: apiUrl,\n\n accessLevels: {\n viewMap: 100,\n aggregatedData: 200,\n viewGeoMessages: 300,\n addGeoMessages: 400,\n addGeoMessageImage: 410,\n addPrivateGeoMessage: 420,\n addPolygons: 500,\n submitRasterData: 515,\n addRestrictedPolygons: 525,\n viewPrivateGeoMessages: 550,\n deleteGeomessages: 600,\n alterOrDeleteCustomPolygons: 700,\n forms: 750,\n customPolygonLayers: 800,\n userManagement: 900,\n owner: 1000,\n\n mapPublicLevelOne: 300, // viewGeoMessages\n mapPublicLevelTwo: 500, // addPolygons\n\n min: 0,\n max: 1000,\n },\n\n get: (url, body, user) => {\n return apiManagerFetch(\"GET\", url, body, user);\n },\n\n post: (url, body, user) => {\n return apiManagerFetch(\"POST\", url, body, user);\n },\n\n fetch: (method, url, body, user) => {\n return apiManagerFetch(method, url, body, user);\n },\n};\n\nfunction CustomError(status, message) {\n var error = Error.call(this, message);\n\n this.name = \"API Error\";\n this.message = error.message;\n this.stack = error.stack;\n this.status = status;\n}\n\nCustomError.prototype = Object.create(Error.prototype);\nCustomError.prototype.constructor = CustomError;\n\nasync function apiManagerFetch(method, url, body, user) {\n url = `${apiUrl}${url}`;\n let headers = {};\n\n if (body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n if (user) {\n headers[\"Authorization\"] = `Bearer ${user.token}`;\n }\n\n let gottenResponse = null;\n let isText = false;\n let isJson = false;\n\n let options = {\n method: method,\n headers: headers,\n };\n\n if (body) {\n options.body = JSON.stringify(body);\n }\n\n return await fetch(url, options)\n .then((response) => {\n if (!response.ok) {\n if (response.status === 429) {\n alert(\n `U heeft de kaart te veel bevraagd. Probeer over een minuut nog eens. Neem contact op met de eigenaar van de kaart om uw limiet te verhogen.`\n );\n }\n\n let err = { status: response.status, message: response.message };\n\n throw err;\n }\n\n gottenResponse = response;\n\n let contentType = response.headers.get(\"Content-Type\");\n\n if (contentType) {\n isText = contentType.includes(\"text\");\n isJson = contentType.includes(\"application/json\");\n } else {\n isText = true;\n }\n\n if (isJson) {\n return response.json();\n } else if (isText) {\n return response.text();\n } else {\n return response.blob();\n }\n })\n .then((result) => {\n if (gottenResponse.status === 200) {\n return result;\n } else {\n if (!isText) {\n throw new CustomError(gottenResponse.status, result.message);\n } else {\n throw new CustomError(gottenResponse.status, result);\n }\n }\n });\n}\n\nexport default ApiManager;\n","import React, { useState } from \"react\";\nimport Typography from \"@material-ui/core/Typography\";\nimport Button from \"@material-ui/core/Button\";\nimport NavigateNextIcon from \"@material-ui/icons/NavigateNext\";\nimport { Paper } from \"@material-ui/core\";\n\nimport choice1 from \"./assets/image1.jpg\";\nimport choice2 from \"./assets/image2.jpg\";\nimport choice3 from \"./assets/image10.jpg\";\nimport choice4 from \"./assets/image3.png\";\nimport choice5 from \"./assets/image4.png\";\nimport choice6 from \"./assets/image6.png\";\nimport choice7 from \"./assets/image7.png\";\nimport choice8 from \"./assets/image8.png\";\n\nexport const AnswerQuestionsStep = ({ onSubmitInfo }) => {\n const [start, setStart] = useState(true);\n\n const handleSubmit = (value) => () => {\n onSubmitInfo({ class: value });\n };\n const handleYesClick = () => {\n setStart(true);\n };\n const handleNoClick = () => {\n console.log(\"NO\");\n onSubmitInfo(null);\n };\n if (!start) {\n return (\n \n \n Bent u deskundig op het terrein van kruidenrijkdom van graslanden, zodat u het grasland kunt typeren (we\n gebruiken de graslandtypen van de veldgids van Schippers et al., 2012).\n \n \n } onClick={handleNoClick}>\n Nee\n \n } onClick={handleYesClick}>\n Ja\n \n
\n \n );\n }\n return (\n \n
\n \n Bent u in staat om voor dit perceel het volgende onderscheid te maken? Zo ja vink dan aan, kies anders nee\n \n \n\n
\n \n 0-A. Engels Raaigrasland \n \n \n glanzend groene productieve grassoorten; Type 0 in Schippers et al., 2012\n \n\n {true ? null :
}
\n \n Select 0-A\n \n \n
\n \n 0-B. Productieve voederkruiden \n \n \n Ingezaaide productieve voederkruiden (productieve grassen met o.a. smalle weegbree, cichorei en div. klavers)\n \n\n {true ? null :
}
\n \n Select 0-B\n \n \n
\n \n 0-C. Productief klavergrasland \n \n Productief klavergrasland (productieve grassen met minimaal 10% klaver) \n\n {true ? null :
}
\n \n Select 0-C\n \n \n
\n \n Geen van de typen 0-A, 0-B of 0-C \n (kies dit alleen als u het onderscheid tussen typen 1-5 niet kent) )\n \n Het grasland voldoet aan geen van de genoemde typen \n\n {true ? null :
}
\n \n Select Geen type\n \n \n\n
\n \n Nee \n \n Ik ben niet in staat deze typering goed te maken. \n {true ? null :
}
\n \n Select Nee\n \n \n\n
\n } onClick={handleNoClick}>\n Sla Over\n \n
\n
\n );\n};\n","import React, { useState } from \"react\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { FormControl, MenuItem, Paper, Select } from \"@material-ui/core\";\nimport Button from \"@material-ui/core/Button\";\nimport DoneIcon from \"@material-ui/icons/Done\";\nimport NavigateNextIcon from \"@material-ui/icons/NavigateNext\";\n\nexport const AnswerQuestionsStep2 = ({ onSubmitInfo }) => {\n const [start, setStart] = useState(false);\n const [answer1, setAnswer1] = useState(null);\n const [answer2, setAnswer2] = useState(null);\n const [answer3, setAnswer3] = useState(null);\n\n const handleGrazingChange = (event) => {\n setAnswer1(event.target.value);\n };\n const handleOverseededChange = (event) => {\n setAnswer2(event.target.value);\n };\n const handleRateChange = (event) => {\n setAnswer3(event.target.value);\n };\n const handleSubmitClick = () => {\n onSubmitInfo({ 1: answer1, 2: answer2, 3: answer3 });\n };\n const handleYesClick = () => {\n setStart(true);\n };\n const handleNoClick = () => {\n onSubmitInfo(null);\n };\n if (!start) {\n return (\n \n Bent u bekend met het beheer of gebruik van dit grasland? \n \n } onClick={handleNoClick}>\n Nee\n \n } onClick={handleYesClick}>\n Ja\n \n
\n \n );\n }\n return (\n \n
Fill: \n\n
Wordt dit perceel begraasd? \n
\n \n Weet ik niet \n \n Er loopt nu graasvee\n \n \n Er zijn tekenen van begrazing (mestflatten, urineplekken)\n \n \n Het is mij bekend dat het perceel soms begraasd wordt\n \n \n Nee\n \n \n \n\n\n
Is dit grasland recentelijk (afgelopen 5 jaar) ingezaaid of doorgezaaid? \n
\n \n Weet ik niet \n Nee \n \n Ja, met productieve grassen en/of klavers\n \n Ja, met productieve overige kruiden \n Ja, met biodiverse kruiden \n \n \n
Hoe productief schat u dit grasland in? \n
\n \n Weet ik niet \n hoog \n middel \n laag \n \n \n
\n }\n onClick={handleSubmitClick}\n >\n Klaar met deze stap\n \n } onClick={handleNoClick}>\n Sla Over\n \n
\n
\n );\n};\n","import React, { useState } from \"react\";\nimport Typography from \"@material-ui/core/Typography\";\nimport Button from \"@material-ui/core/Button\";\nimport NavigateNextIcon from \"@material-ui/icons/NavigateNext\";\nimport { Paper } from \"@material-ui/core\";\n\nimport choice1 from \"./assets/image1.jpg\";\nimport choice2 from \"./assets/image2.jpg\";\nimport choice3 from \"./assets/image10.jpg\";\nimport choice4 from \"./assets/image3.png\";\nimport choice5 from \"./assets/image4.png\";\nimport choice6 from \"./assets/image6.png\";\nimport choice7 from \"./assets/image7.png\";\nimport choice8 from \"./assets/image8.png\";\n\nexport const AnswerQuestionsStepb = ({ onSubmitInfo }) => {\n const [start, setStart] = useState(true);\n\n const handleSubmit = (value) => () => {\n onSubmitInfo({ class: value });\n };\n const handleYesClick = () => {\n setStart(true);\n };\n const handleNoClick = () => {\n onSubmitInfo(null);\n };\n if (!start) {\n return (\n \n \n Bent u deskundig op het terrein van kruidenrijkdom van graslanden, zodat u het grasland kunt typeren (we\n gebruiken de graslandtypen van de veldgids van Schippers et al., 2012).\n \n \n } onClick={handleNoClick}>\n Nee\n \n } onClick={handleYesClick}>\n Ja\n \n
\n \n );\n }\n return (\n \n
\n \n Bent u bekend met de indeling van Wim Schippers’ veldgids uit 2012? Zo ja vink dat dan het juiste type aan,\n kies anders nee\n \n \n\n
\n \n 1. Grassen-mix (Type 1 in Schippers, 2012)\n \n\n {true ? null :
}
\n \n Select 1\n \n \n
\n \n 2. Dominant stadium (Type 2 in Schippers, 2012)\n \n\n {true ? null :
}
\n \n Select 2\n \n \n
\n \n 3. Gras-kruiden-mix (Type 3 in Schippers, 2012)\n \n\n {true ? null :
}
\n \n Select 3\n \n \n
\n \n 4. Bloemrijk grasland (Type 4 in Schippers, 2012)\n \n\n {true ? null :
}
\n \n Select 4\n \n \n
\n \n 5. Schraalland (Type 5 in Schippers, 2012)\n \n\n {true ? null :
}
\n \n Select 5\n \n \n\n
\n \n 6. Nee ik ben hier niet mee bekend \n \n\n {true ? null :
}
\n \n Select 5\n \n \n\n
\n } onClick={handleNoClick}>\n Sla Over\n \n
\n
\n );\n};\n","import React, { Component } from \"react\";\n\nimport Button from \"@material-ui/core/Button\";\nimport { makeStyles } from \"@material-ui/core/styles\";\n\nimport AddAPhotoIcon from \"@material-ui/icons/AddAPhoto\";\n\nimport AppUtility from \"../../AppUtility\";\nimport MuiAlert from \"@material-ui/lab/Alert\";\n\nimport \"./AddPhoto.css\";\nimport { Paper, Snackbar, Typography } from \"@material-ui/core\";\nimport ApiManager from \"../../ApiManager\";\nimport PublishIcon from \"@material-ui/icons/Publish\";\nimport CancelIcon from \"@material-ui/icons/Cancel\";\nimport NavigateNextIcon from \"@material-ui/icons/NavigateNext\";\n\nimport { createMuiTheme, ThemeProvider } from \"@material-ui/core/styles\";\nimport { blue } from \"@material-ui/core/colors\";\nimport { AnswerQuestionsStep } from \"./AnswerQuestionsStep\";\nimport { AnswerQuestionsStep2 } from \"./AnswerQuestionsStep2\";\nimport { AnswerQuestionsStepb } from \"./AnswerQuestionsStepb\";\n\nimport example1 from \"./assets/image5.png\";\nimport example2 from \"./assets/image9.png\";\n\nfunction Alert(props) {\n return ;\n}\n\nconst useStyles = makeStyles(\n (theme) => ({\n listItem: {\n display: \"flex\",\n gap: \"6px\",\n },\n }),\n { name: \"AddPhoto\" }\n);\n\nconst Instructions = ({ type, canProceed, count }) => {\n const styles = useStyles();\n if (type === 0) {\n return (\n \n \n -\n \n Vanaf de rand van het perceel, neemt u de foto’s staand en op ooghoogte\n \n
\n \n -Neem 2 foto’s en vermijd daarbij overlap \n
\n \n -Zorg dat u een klein stukje horizon of lucht op de foto zet \n
\n \n -\n \n Vermijd storende/afwijkende objecten op de foto, zoals een hekwerk of koe\n \n
\n \n -Vermijd de perceelrand en/of de slootkant \n
\n \n -Voorbeeld van een goede overzichtsfoto: \n
\n \n
\n
\n \n );\n }\n return (\n \n \n -\n \n Richt de camera meer naar beneden voor het vastleggen van plantensoorten\n \n
\n \n -Maak representatieve fotos niet alleen de mooiste stukjes \n
\n \n -Neem 2 foto’s en vermijd daarbij overlap \n
\n \n -\n \n Als u daarvoor toestemming heeft en geen dieren verstoort , kunt u in het perceel gaan staan en meerdere\n detailfoto’s maken op verschillende plekken in het perceel\n \n
\n \n -\n \n Maak niet alleen foto’s van de allermooiste stukjes, maar juist van representatieve gedeelten\n \n
\n \n -Vermijd storende objecten zoals uw eigen schoenen \n
\n \n -Vermijd overlap tussen de foto’s \n
\n \n
\n
\n \n );\n};\n\nconst Confirmation = ({ src, handleConfirmClick, handleRejectClick }) => {\n return (\n \n
\n
\n } onClick={handleRejectClick}>\n Opnieuw\n \n\n }\n onClick={handleConfirmClick}\n >\n Gebruik deze foto\n \n
\n
\n );\n};\n\nconst theme = createMuiTheme({ palette: { primary: blue } });\n\nconst ProceedButton = ({ visible, handleProceedClick }) => {\n if (!visible) {\n return null;\n }\n return (\n \n \n } onClick={handleProceedClick}>\n Klaar met deze stap\n \n \n
\n );\n};\n\nconst UploadButton = ({ step, type, onPhotoChange }) => {\n return (\n \n \n \n }>\n Maak {type ? \"Detailfoto\" : \"Overzichtfoto\"} {step}\n \n \n
\n );\n};\n\nclass AddPhoto extends Component {\n constructor(props) {\n super(props);\n this.state = { step: 1, open: false, photos: [[], []] };\n }\n\n handleClose = (event, reason) => {\n if (reason === \"clickaway\") {\n return;\n }\n\n this.setState({ open: false });\n };\n handleSubmit = (message, complete, answerType) => {\n const body = {\n text: JSON.stringify({ ...message, session: this.props.sessionId, answerType }),\n location: {\n x: this.props.position.coords[1],\n y: this.props.position.coords[0],\n },\n };\n\n body.mapId = this.props.extMap.id;\n body.layerId = \"e13b1302-2c96-445d-adf7-611c83f985eb\";\n body.geometryId = this.props.addedFeature.properties.id;\n\n ApiManager.post(\"/message/add\", body, this.props.user)\n .then(() => {\n complete && this.props.onActionPaneModeChange(AppUtility.actionPaneMode.none);\n })\n .catch((err) => {\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n };\n handleSubmitInfo1 = (message) => {\n this.props.onTypeChange();\n message && this.handleSubmit(message, false, 1);\n };\n handleSubmitInfo2 = (message) => {\n this.props.onOpenSnackbar();\n if (message) {\n this.handleSubmit(message, true, 2);\n } else {\n this.props.onActionPaneModeChange(AppUtility.actionPaneMode.none);\n }\n };\n handleProceedClick = () => {\n this.setState({ step: 1 });\n this.props.onTypeChange();\n };\n handleRejectClick = () => {\n this.setState({ confirm: null });\n };\n\n handleConfirmClick = (base64) => {\n const photos = [...this.state.photos];\n photos[this.props.type].push(base64);\n\n const body = {\n image: base64,\n text: JSON.stringify({\n session: this.props.sessionId,\n photoType: this.props.type ? \"Detailfoto\" : \"Overzichtfoto\",\n }),\n location: {\n x: this.props.position.coords[1],\n y: this.props.position.coords[0],\n },\n };\n\n body.mapId = this.props.extMap.id;\n body.layerId = \"e13b1302-2c96-445d-adf7-611c83f985eb\";\n body.geometryId = this.props.addedFeature.properties.id;\n\n ApiManager.post(\"/message/add\", body, this.props.user)\n .then((result) => {\n // this.setState({ open: true, snackbarOpen: true });\n })\n .catch((err) => {\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n this.setState({ step: this.state.step + 1, photos, confirm: null });\n };\n onPhotoChange = (e) => {\n let file = e.target.files[0];\n\n if (!file.type.includes(\"image\")) {\n alert(\"Foto moet een plaatje zijn.\");\n return;\n }\n\n toBase64(file)\n .then((base64) => {\n console.log(\"uploaded\");\n console.log(base64);\n this.handleConfirmClick(base64);\n this.setState({ confirm: base64 });\n })\n .catch((err) => {\n alert(`Foutief bestand. Probeer nog eens of een ander bestand.`);\n });\n };\n\n render() {\n if (this.props.type === 2) {\n return ;\n }\n if (this.props.type === 3) {\n return ;\n }\n\n if (this.props.type === 4) {\n return ;\n }\n\n const canProceed = this.state.photos[this.props.type] && this.state.photos[this.props.type].length > 1;\n return (\n <>\n \n \n \n {\n this.setState({ snackbarOpen: false });\n }}\n anchorOrigin={{ vertical: \"top\", horizontal: \"center\" }}\n >\n Upload gelukt! \n \n >\n );\n }\n}\n\nconst toBase64 = (file) =>\n new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(file);\n\n reader.onload = () => resolve(reader.result);\n reader.onerror = (error) => reject(error);\n });\n\nexport default AddPhoto;\n","import React, { Component } from \"react\";\n\nimport Button from \"@material-ui/core/Button\";\nimport TouchAppIcon from \"@material-ui/icons/TouchApp\";\nimport ControlCameraIcon from \"@material-ui/icons/ControlCamera\";\nimport AppUtility from \"../../AppUtility\";\n\nimport \"./PostPhoto.css\";\nimport { Paper, Typography } from \"@material-ui/core\";\n\nclass PostPhoto extends Component {\n state = { manualSelect: false };\n\n componentDidUpdate(prevProps) {\n if (this.props.selectedFeature && prevProps.selectedFeature !== this.props.selectedFeature) {\n this.props.onActionPaneModeChange(AppUtility.actionPaneMode.addPhoto);\n }\n }\n\n handleManualButtonClick = () => {\n this.setState({ manualSelect: true });\n this.props.onLocationSelect(true);\n };\n handleSubmitButtonClick = () => {\n this.props.onActionPaneModeChange(AppUtility.actionPaneMode.addPhoto, this.state.manualSelect);\n };\n\n render() {\n if (this.state.manualSelect) {\n return (\n <>\n \n \n Drag map to point \n \n\n \n \n Select and Proceed\n \n
\n >\n );\n }\n return (\n <>\n \n \n Selecteer perceel op kaart \n \n\n \n \n Perceel is niet zichtbaar\n \n
\n >\n );\n }\n}\n\nexport default PostPhoto;\n","import React, { Component } from \"react\";\nimport moment from \"moment\";\n\nimport Stepper from \"@material-ui/core/Stepper\";\nimport Step from \"@material-ui/core/Step\";\nimport StepLabel from \"@material-ui/core/StepLabel\";\nimport StepContent from \"@material-ui/core/StepContent\";\nimport Typography from \"@material-ui/core/Typography\";\nimport IconButton from \"@material-ui/core/IconButton\";\n\nimport DeleteIcon from \"@material-ui/icons/Delete\";\n\nimport ApiManager from \"../../ApiManager\";\n\nimport \"./Timeline.css\";\n\nconst getMessageData = (message) => {\n try {\n return JSON.parse(message.text);\n } catch {\n return {};\n }\n};\nconst combineMessages = (messages) => {\n const map = {};\n const result = [];\n messages.forEach((message) => {\n const data = getMessageData(message);\n if (data.session) {\n if (!map[data.session]) {\n map[data.session] = [];\n }\n map[data.session].push(message);\n } else {\n result.push(message);\n }\n });\n Object.keys(map).forEach((session) => {\n const items = map[session];\n const descriptionItem = items.find((item) => !item.image);\n const group = { ...descriptionItem, group: true };\n group.images = items.map((item) => item).filter((item) => !!item.image);\n group.textMessages = items.map((item) => item).filter((item) => !item.image);\n result.push(group);\n });\n return result.sort((a, b) => {\n const aDate = moment(a.date);\n const bDate = moment(b.date);\n\n if (aDate.isBefore(bDate)) {\n return 1;\n }\n if (aDate.isAfter(bDate)) {\n return -1;\n }\n return 0;\n });\n};\n\nclass Timeline extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n geoMessages: null,\n };\n }\n\n componentDidMount() {\n console.log('MOUNTING timeline')\n console.log(this.props)\n this.getGeoMessages();\n }\n\n getMessages = async (mapId, layerId, elementId) => {\n let body = {\n mapId,\n layerId,\n geometryIds:[elementId],\n };\n\n const { result } = await ApiManager.post(\"/message/get\", body, this.props.user);\n return result;\n };\n getGeoMessages = async () => {\n const { selectedFeature } = this.props;\n console.log('getting messages for')\n console.log(selectedFeature)\n let geoMessages\n try {\n geoMessages = await this.getMessages(selectedFeature.properties.mapId, selectedFeature.properties.layerId, selectedFeature.properties.id);\n console.log('found messages')\n console.log(geoMessages)\n } catch (err) {\n alert(\"Er is een fout opgetreden. Herlaadt de pagina en probeer nog eens.\");\n this.setState({ geoMessages: [] });\n }\n let combined = combineMessages(geoMessages)\n console.log(combined)\n this.setState({ geoMessages: combined });\n\n\n };\n\n viewImage = (geoMessage) => {\n if (geoMessage.fullImage) {\n this.props.onModalChange( );\n return;\n }\n\n let body = {\n mapId: this.props.selectedFeature.properties.mapId,\n layerId: this.props.selectedFeature.properties.layerId,\n messageId: geoMessage.id,\n };\n ApiManager.post(\"/message/image\", body, this.props.user)\n .then((fullImage) => {\n let reader = new FileReader();\n reader.readAsDataURL(fullImage);\n reader.onloadend = () => {\n geoMessage.fullImage = reader.result;\n this.viewImage(geoMessage);\n };\n })\n .catch((err) => {\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n };\n\n deleteMessage = async (geoMessage) => {\n const body = {\n mapId: this.props.selectedFeature.properties.mapId,\n layerId: this.props.selectedFeature.properties.layerId,\n messageId: geoMessage.id,\n };\n return ApiManager.post(\"/message/delete\", body, this.props.user);\n };\n onDelete = (geoMessage) => {\n const user = geoMessage.user || geoMessage.images[0].user;\n let confirm = window.confirm(`Weet u zeker dat u deze foto van \"${user.username}\" wilt deleten?`);\n\n if (!confirm) {\n return;\n }\n\n if (geoMessage.group) {\n const promises = geoMessage.images.map((image) => this.deleteMessage(image));\n if (geoMessage.id) {\n promises.push(this.deleteMessage(geoMessage));\n }\n\n Promise.all(promises)\n .then(() => {\n const remainingGeoMessages = this.state.geoMessages.filter((x) => x.id !== geoMessage.id);\n this.setState({ geoMessages: remainingGeoMessages });\n })\n .catch((err) => {\n console.error(err);\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n return;\n }\n this.deleteMessage(geoMessage)\n .then(() => {\n const remainingGeoMessages = this.state.geoMessages.filter((x) => x.id !== geoMessage.id);\n this.setState({ geoMessages: remainingGeoMessages });\n })\n .catch((err) => {\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n };\n\n render() {\n const { user, map } = this.props;\n\n let timelineItems = null;\n if (this.state.geoMessages) {\n timelineItems = this.state.geoMessages.map((geoMessage) => {\n let canDelete = map.accessLevel >= 600 || (user && geoMessage.user && geoMessage.user.username === user.username);\n\n return (\n \n \n {\n \n
\n {geoMessage.user ? geoMessage.user.username : null}\n {canDelete ? (\n this.onDelete(geoMessage)}\n >\n \n \n ) : null}\n
\n
{moment(geoMessage.date).format(\"DD-MM-YYYY HH:mm\")}
\n
\n }\n \n {geoMessage.group ? (\n \n {geoMessage.textMessages.map((message) => {\n const data = getMessageData(message);\n if (data.answerType === 1) {\n return (\n \n \n Class: {data.class} \n \n \n );\n }\n return (\n \n \n 1: {data[1]} \n \n \n 2: {data[2]} \n \n \n 3: {data[3]} \n \n \n );\n })}\n \n {geoMessage.images.map((image) => (\n
\n
this.viewImage(image)} />\n
\n ))}\n
\n \n ) : (\n \n {geoMessage.message} \n {geoMessage.image ? (\n this.viewImage(geoMessage)} />\n ) : null}\n \n )}\n \n );\n });\n }\n\n return (\n \n {timelineItems} \n
\n );\n }\n}\n\nexport default Timeline;\n","import React, { Component } from \"react\";\n\nimport Paper from \"@material-ui/core/Paper\";\nimport IconButton from \"@material-ui/core/IconButton\";\n\nimport CloseIcon from \"@material-ui/icons/Close\";\n\nimport AddPhoto from \"./AddPhoto/AddPhoto\";\nimport PostPhoto from \"./PostPhoto/PostPhoto\";\nimport Timeline from \"./Timeline/Timeline\";\n\nimport AppUtility from \"../AppUtility\";\n\nimport \"./ActionPane.css\";\nimport { Typography } from \"@material-ui/core\";\n\nconst LetOp = () => {\n return (\n \n \n Let op: het is broedseizoen, betreed kruidenrijk grasland percelen niet of zo min mogelijk en alleen met\n toestemming van de eigenaar van de percelen\n \n \n );\n};\n\nclass ActionPane extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n photoBase64: null,\n type: 0,\n };\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.actionPaneMode !== this.props.actionPaneMode) {\n this.setState({ prevActionPaneMode: prevProps.actionPaneMode });\n }\n }\n\n onCloseActionPane = () => {\n this.setState({ photoBase64: null });\n this.props.onActionPaneModeChange(AppUtility.actionPaneMode.none);\n ///\n };\n\n handleTypeChange = () => {\n this.setState({ type: this.state.type + 1 });\n };\n\n render() {\n let mode = this.props.actionPaneMode;\n console.log(mode);\n // First step\n if (mode === AppUtility.actionPaneMode.postPhoto) {\n return (\n \n \n Select Location \n \n \n \n
\n \n \n );\n }\n\n // Second step\n if (mode === AppUtility.actionPaneMode.addPhoto) {\n let caption = \"Stap 1: Overzichtfotos\";\n if (this.state.type === 1) {\n caption = \"Stap 2: Detailfotos\";\n }\n if (this.state.type === 2) {\n caption = \"Stap 3a: Classificatie\";\n }\n if (this.state.type === 3) {\n caption = \"Stap 3b: Classificatie\";\n }\n if (this.state.type === 4) {\n caption = \"Stap 4: Productiviteit van grasland\";\n }\n return (\n \n \n {caption} \n \n \n \n
\n \n {this.state.type === 1 ?
: null}\n
\n
\n \n );\n }\n\n // Results on timeline\n return (\n \n \n
{`Perceel ${this.props.selectedFeature.properties.id}`} \n \n \n \n \n \n map.id === this.props.selectedFeature.properties.mapId\n )}\n />\n
\n \n );\n }\n}\n\nexport default ActionPane;\n","import React, { Component } from 'react';\n\nimport IconButton from '@material-ui/core/IconButton';\n\nimport CloseIcon from '@material-ui/icons/Close';\n\nimport ApiManager from '../ApiManager';\n\nimport './ModalView.css';\nimport { Icon } from '@material-ui/core';\n\nclass ModalView extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n uploading: false,\n message: ''\n };\n }\n \n componentDidMount() {\n\n }\n\n closeModal = () => {\n this.props.onModalChange(null);\n }\n\n render() {\n let children = this.props.children;\n\n if (!children) {\n return null;\n }\n\n return (\n \n
\n \n \n \n
\n
\n {children}\n
\n
\n );\n }\n}\n\nexport default ModalView;\n","import React, { Component } from \"react\";\n\nimport { createMuiTheme, ThemeProvider } from \"@material-ui/core/styles\";\nimport Button from \"@material-ui/core/Button\";\nimport IconButton from \"@material-ui/core/Button\";\nimport { Paper, Snackbar, Typography } from \"@material-ui/core\";\n\nimport DefaultTheme from \"./theme\";\n\nimport AddIcon from \"@material-ui/icons/Add\";\nimport bbox from \"geojson-bbox\";\nimport Map from \"./Map/Map\";\nimport Navbar from \"./Navbar/Navbar\";\nimport ActionPane from \"./ActionPane/ActionPane\";\nimport ModalView from \"./ModalView/ModalView\";\n\nimport ApiManager from \"./ApiManager\";\nimport AppUtility from \"./AppUtility\";\n\nimport \"./App.css\";\nimport moment from \"moment\";\nimport MuiAlert from \"@material-ui/lab/Alert\";\nconst theme = createMuiTheme(DefaultTheme);\n\nconst ACCOUNTS_URL = \"https://account.ellipsis-drive.com/v2\";\n// const ACCOUNTS_URL = \"https://dev.account.ellipsis-drive.com\";\n// const ACCOUNTS_URL = 'http://localhost:3001';\n\nconst LOCAL_STORE_USER_KEY = \"user\";\n\nexport const PLOT_FREE_AREA_MAP_ID = \"cee89344-dfe2-41b1-a9ea-c1f4acdf8bfa\";\nconst PLOT_NETHERLANDS_AREA_MAP_ID = \"60b634e1-8dfd-4612-bfe2-b828c81e8dce\";\nconst PERCELEN_LAYER_ID = \"e3a7b9bf-dba4-4d4f-9738-954973ab0c86\";\nconst FREE_PERCELEN_LAYER_ID = \"e13b1302-2c96-445d-adf7-611c83f985eb\";\n\nconst GEOMESSAGE_LIMIT = 100;\nconst NEARBY_FIELDS_METER = 15;\nconst NEARBY_FIELDS_ACCURACY_MODIFIER = 1;\nfunction Alert(props) {\n return ;\n}\n\nclass App extends Component {\n locationError = false;\n gettingGeometries = false;\n freezeGeometries = false;\n lastGetGeometriesCall = null;\n\n constructor(props) {\n super(props);\n\n this.dev = false;\n let accountsUrl = \"\";\n\n if (!this.dev) {\n accountsUrl = \"https://account.ellipsis-drive.com/\";\n } else {\n let url = window.location.href;\n let split = url.split(\":\");\n accountsUrl = `${split[0]}:${split[1]}:${parseInt(split[2].split(\"/\")[0]) + 1}/`;\n }\n\n this.state = {\n init: null,\n\n user: null,\n\n accountsUrl: accountsUrl,\n openAccount: false,\n\n actionPaneMode: AppUtility.actionPaneMode.none,\n\n geoJsons: [],\n floatingMessage: null,\n\n openAddTooltip: false,\n sessionId: null,\n selectLocation: false,\n };\n }\n\n componentDidMount() {\n this.retrieveUser(() => {\n this.initMapData(() => {\n window.addEventListener(\"message\", this.onAccountAction, false);\n\n this.setState({ init: true });\n });\n });\n\n let locationOpts = { enableHighAccuracy: true };\n\n navigator.geolocation.getCurrentPosition(this.onLocationChange, this.onLocationChangeErr, locationOpts);\n navigator.geolocation.watchPosition(this.onLocationChange, this.onLocationChangeErr, locationOpts);\n\n if (this.state.user) {\n this.validateUser();\n }\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (this.state.user) {\n if (!prevState.user || this.state.user.id !== prevState.user.id) {\n this.validateUser();\n }\n }\n }\n\n validateUser = () => {\n ApiManager.get(\"/account/info\", null, this.state.user)\n .then(() => {\n console.log(\"user valid\");\n })\n .catch(() => {\n console.log(\"user not valid\");\n this.onLogout();\n });\n };\n\n initAccount = async () => {\n let initObject = { type: \"init\", dev: false };\n if (this.state.user) {\n initObject.data = this.state.user;\n }\n let iframe = document.getElementById(\"account\");\n\n iframe.contentWindow.postMessage(initObject, ACCOUNTS_URL);\n };\n\n onLocationChange = (pos) => {\n let newPos = {\n coords: [pos.coords.latitude, pos.coords.longitude],\n accuracy: pos.coords.accuracy,\n };\n\n this.setState({ position: newPos }, () => {\n if (this.state.actionPaneMode === AppUtility.actionPaneMode.postPhoto) {\n this.getGeometriesNearPos();\n }\n });\n };\n\n onLocationChangeErr = (err) => {\n if (!this.locationError) {\n alert(\n `Er is een probleem opgetreden bij het ophalen van uw locatie. Controleer of u de app toestemming heeft gegeven tot uw locatie. Code: ${\n err.code\n }, ${err[err.code] ? err[err.code] : err.message}`\n );\n }\n\n console.log(err);\n\n this.locationError = true;\n };\n\n retrieveUser = async (cb) => {\n let user = null;\n let userJson = localStorage.getItem(LOCAL_STORE_USER_KEY);\n\n if (!userJson) {\n if (cb) {\n cb();\n }\n return;\n }\n\n user = JSON.parse(userJson);\n\n ApiManager.get(\"/account/validateLogin\", null, user)\n .then(() => {\n if (user.username) {\n user.username = user.username.toLowerCase();\n }\n\n this.setState({ user: user }, cb);\n })\n .catch((err) => {\n console.error(err);\n localStorage.removeItem(LOCAL_STORE_USER_KEY);\n\n this.setState({ user: null }, cb);\n });\n };\n\n initMapData = async (cb) => {\n const { user } = this.state;\n\n let promises = [\n ApiManager.post(\n \"/account/shapes\",\n { access: [\"subscribed\", \"public\", \"owned\"], name: \"fotos aan percelen (2021)\", pageSize: 20 },\n user\n ),\n\n ApiManager.post(\"/metadata\", { mapId: PLOT_NETHERLANDS_AREA_MAP_ID }, user),\n ApiManager.post(\n \"/account/shapes\",\n { access: [\"subscribed\", \"public\", \"owned\"], name: \"fotos aan locaties (2021)\", pageSize: 20 },\n user\n ),\n ApiManager.post(\"/metadata\", { mapId: PLOT_FREE_AREA_MAP_ID }, user),\n ];\n\n await Promise.all(promises)\n .then((results) => {\n const [maps, metadata, freeAccessMap, freeAccessMapMetadata] = results;\n let plotsNetherlandsMap = maps.result.find((x) => x.id === PLOT_NETHERLANDS_AREA_MAP_ID);\n let extMap = freeAccessMap.result.find((x) => x.id === PLOT_FREE_AREA_MAP_ID);\n if (!plotsNetherlandsMap || !extMap) {\n let err = {\n status: 500,\n message: \"Target map was not found in API result.\",\n };\n throw err;\n }\n\n plotsNetherlandsMap.metadata = metadata;\n extMap.metadata = freeAccessMapMetadata;\n\n let targetPolygonLayer = plotsNetherlandsMap.metadata.geometryLayers.find((x) => x.id === PERCELEN_LAYER_ID);\n\n if (!targetPolygonLayer) {\n let err = {\n status: 500,\n message: \"Target polygon layer was not found in API result.\",\n };\n throw err;\n }\n\n this.setState({ map: plotsNetherlandsMap, extMap }, cb);\n })\n .catch((err) => {\n console.log(err);\n alert(`Er is een fout opgetreden. Herlaadt de pagina en probeer opnieuw.`);\n });\n };\n getMessages = async (mapId, layerId) => {\n return [];\n const body = {\n mapId: mapId,\n layerId: layerId,\n pageSize: 100,\n };\n\n const { result } = await ApiManager.post(\"/message/get\", body, this.state.user);\n console.log(result);\n return result;\n };\n\n getGeometry = async (mapId, layerId, bounds) => {\n let body = {\n mapId,\n layerId: layerId,\n bounds,\n };\n\n const { result } = await ApiManager.post(\"/geometry/get\", body, this.state.user);\n return result.features.map((item) => {\n item.properties.mapId = mapId;\n item.properties.layerId = layerId;\n return item;\n });\n };\n getFeatures = async (mapId, geometryIds, layerId) => {\n const body = { mapId: mapId, layerId: layerId, geometryIds: geometryIds };\n\n const { result } = await ApiManager.post(\"/geometry/ids\", body, this.state.user);\n return result.features.map((item) => {\n item.properties.mapId = mapId;\n item.properties.layerId = layerId;\n return item;\n });\n };\n getGeometries = async (bounds, all, cb) => {\n if (this.gettingGeometries) {\n return;\n }\n\n if (this.freezeGeometries) {\n this.lastGetGeometriesCall = () => this.getGeometries(bounds, all, cb);\n return;\n }\n\n this.gettingGeometries = true;\n\n const { map, extMap } = this.state;\n\n let geometryIds = [];\n let geoJsons = [];\n let floatingMessage = \"\";\n\n const geoMessages = await this.getMessages(map.id, PERCELEN_LAYER_ID);\n const extGeoMessages = await this.getMessages(this.state.extMap.id, FREE_PERCELEN_LAYER_ID);\n\n const allMessages = [...geoMessages, ...extGeoMessages];\n if (allMessages) {\n for (let i = 0; i < allMessages.length; i++) {\n const message = allMessages[i];\n\n if (!geometryIds.includes(message.geometryId)) {\n geometryIds.push(message.geometryId);\n }\n }\n }\n\n this.setState({ geoMessages: allMessages });\n\n if (geometryIds.length) {\n const featureCollection = await this.getFeatures(map.id, geometryIds, PERCELEN_LAYER_ID);\n const featureCollection2 = await this.getFeatures(extMap.id, geometryIds, FREE_PERCELEN_LAYER_ID);\n\n geoJsons = [...featureCollection, ...featureCollection2];\n\n floatingMessage = `Getoond: Percelen met meest recente foto's`;\n } else {\n floatingMessage = \"Geen berichten gevonden binnen het scherm\";\n }\n\n this.setState(\n {\n geoJsons: geoJsons,\n floatingMessage: floatingMessage,\n },\n cb\n );\n\n this.gettingGeometries = false;\n };\n\n getGeometriesNearPos = async () => {\n let position = this.state.position;\n\n let long = position.coords[1];\n let lat = position.coords[0];\n\n let maxDeviation = NEARBY_FIELDS_METER + NEARBY_FIELDS_ACCURACY_MODIFIER * position.accuracy;\n if (maxDeviation > 1000) {\n maxDeviation = 1000;\n }\n maxDeviation = 500;\n let longDiff = (360 / (40000000 * Math.cos((2 * Math.PI * lat) / 360))) * maxDeviation;\n let latDiff = (360 / 40000000) * maxDeviation;\n\n let bounds = {\n xMin: long - longDiff,\n xMax: long + longDiff,\n yMin: lat - latDiff,\n yMax: lat + latDiff,\n };\n\n let cb = () => {\n let geoJsons = this.state.geoJsons;\n\n this.setState({ nearbyBounds: bounds });\n\n if (!this.state.selectedFeature) {\n if (geoJsons.length > 0) {\n this.setState({\n floatingMessage: \"Selecteer het perceel waarvoor de foto is\",\n });\n } else {\n this.setState({\n floatingMessage: \"Geen percelen in de buurt. Ga dichter bij een perceel staan.\",\n });\n }\n }\n };\n\n let body = {\n mapId: PLOT_NETHERLANDS_AREA_MAP_ID,\n layerId: PERCELEN_LAYER_ID,\n bounds: bounds,\n pageSize: 30,\n };\n\n ApiManager.post(\"/geometry/bounds\", body, this.state.user)\n .then((r) => {\n console.log(r);\n let result = r.result.features.map((item) => {\n item.properties.mapId = PLOT_NETHERLANDS_AREA_MAP_ID;\n item.properties.layerId = PERCELEN_LAYER_ID;\n return item;\n });\n this.setState({ geoJsons: result }, cb);\n })\n .catch((e) => {\n console.log(e);\n });\n };\n\n onAccountAction = (event) => {\n if (event.data.type && event.data.type === \"login\") {\n this.onLogin(event.data.data);\n } else if (event.data.type && event.data.type === \"logout\") {\n this.onLogout();\n } else if (event.data.type && event.data.type === \"overlayClose\") {\n this.setState({ openAccount: false });\n }\n };\n\n onOpenAccount = (open) => {\n if (this.state.openAccount !== open) {\n this.setState({ openAccount: open });\n }\n };\n\n onLogin = (user) => {\n localStorage.setItem(LOCAL_STORE_USER_KEY, JSON.stringify(user));\n\n this.setState({ user: user }, this.initMapData);\n };\n\n onLogout = () => {\n localStorage.removeItem(LOCAL_STORE_USER_KEY);\n\n this.setState(\n {\n user: null,\n actionPaneMode: AppUtility.actionPaneMode.none,\n selectedFeature: null,\n },\n this.initMapData\n );\n };\n\n onMapBoundsChange = (mapBounds, mapCenter, hard = false) => {\n if (this.state.actionPaneMode !== AppUtility.actionPaneMode.postPhoto) {\n this.setState({ mapBounds });\n if (hard) {\n this.getGeometries(mapBounds, false);\n }\n } else {\n this.setState({ mapCenter });\n }\n };\n\n handleLocationSelect = (selectLocation) => {\n this.setState({ selectLocation });\n };\n onAddPhotoClick = () => {\n let user = this.state.user;\n\n if (!user) {\n this.onOpenAccount(true);\n return;\n }\n\n if (!this.state.position) {\n alert(\"Uw locatie is niet gevonden. Sta toe dat deze app uw locatie kan opvragen en probeer daarna nog eens.\");\n return;\n }\n\n this.setState({ sessionId: moment().format() + \"_\" + user.username });\n this.onActionPaneModeChange(AppUtility.actionPaneMode.postPhoto);\n };\n\n onActionPaneModeChange = (mode, selectLocation) => {\n if (this.state.actionPaneMode === mode) {\n return;\n }\n this.setState({ actionPaneMode: mode, selectLocation: false });\n\n if (mode === AppUtility.actionPaneMode.addPhoto && selectLocation) {\n let lng = this.state.mapCenter.lng;\n let lat = this.state.mapCenter.lat;\n this.submitNewPlot(lng, lat);\n return;\n }\n if (mode === AppUtility.actionPaneMode.postPhoto) {\n this.getGeometriesNearPos();\n return;\n }\n if (mode === AppUtility.actionPaneMode.none) {\n this.setState({\n floatingMessage: null,\n geoJsons: [],\n selectedFeature: null,\n });\n this.getGeometries(this.state.mapBounds);\n }\n };\n\n submitNewPlot = (lng, lat) => {\n const layer = this.state.extMap.metadata.geometryLayers.find((x) => x.id === FREE_PERCELEN_LAYER_ID);\n const feature = {\n type: \"Feature\",\n geometry: {\n type: \"Point\",\n coordinates: [lng, lat],\n },\n properties: {},\n };\n const body = {\n mapId: this.state.extMap.id,\n layerId: FREE_PERCELEN_LAYER_ID,\n features: [feature],\n };\n ApiManager.post(\"/geometry/add\", body, this.state.user)\n .then((result) => {\n const [addedId] = result;\n feature.properties.id = addedId;\n this.setState({ addedFeature: feature });\n })\n .catch((err) => {\n alert(\"Er is een fout opgetreden. Probeer nog eens.\");\n });\n };\n\n onShowFloatingMessage = (message) => {\n this.setState({ floatingMessage: message });\n };\n\n onFeatureClick = (feature) => {\n if (this.state.selectedFeature !== feature) {\n this.setState({ selectedFeature: feature });\n }\n\n if (this.state.actionPaneMode === AppUtility.actionPaneMode.addPhoto) {\n let box = bbox(feature);\n\n let lat = (box[0] + box[2]) / 2;\n let lon = (box[1] + box[3]) / 2;\n this.submitNewPlot(lat, lon);\n } else if (this.state.actionPaneMode === AppUtility.actionPaneMode.postPhoto) {\n this.setState({ floatingMessage: null });\n } else if (this.state.actionPaneMode === AppUtility.actionPaneMode.none) {\n this.onActionPaneModeChange(AppUtility.actionPaneMode.timeline);\n }\n };\n\n onFreezeGeometries = (freeze) => {\n this.freezeGeometries = freeze;\n if (this.lastGetGeometriesCall && !freeze) {\n this.lastGetGeometriesCall();\n this.lastGetGeometriesCall = null;\n }\n };\n\n onModalChange = (content) => {\n if (this.state.modalContent !== content) {\n this.setState({ modalContent: content });\n }\n };\n\n render() {\n if (!this.state.init) {\n return null;\n }\n\n let user = this.state.user;\n\n if (this.state.openAccount) {\n let initObject = { type: \"init\" };\n if (this.state.user) {\n initObject.data = this.state.user;\n }\n let iframe = document.getElementById(\"account\");\n if (iframe) {\n iframe.contentWindow.postMessage(initObject, ACCOUNTS_URL);\n }\n }\n\n let commonProps = {\n user: user,\n map: this.state.map,\n\n actionPaneMode: this.state.actionPaneMode,\n selectedFeature: this.state.selectedFeature,\n\n position: this.state.position,\n mapBounds: this.state.mapBounds,\n geoJsons: this.state.geoJsons,\n geoMessages: this.state.geoMessages,\n\n nearbyBounds: this.state.nearbyBounds,\n\n onOpenAccount: this.onOpenAccount,\n onActionPaneModeChange: this.onActionPaneModeChange,\n onShowFloatingMessage: this.onShowFloatingMessage,\n onFeatureClick: this.onFeatureClick,\n onFreezeGeometries: this.onFreezeGeometries,\n onModalChange: this.onModalChange,\n };\n\n let floatingMessage = null;\n if (this.state.floatingMessage) {\n floatingMessage = (\n \n \n {this.state.floatingMessage}\n \n
\n );\n }\n\n return (\n \n \n \n \n
\n {floatingMessage}\n \n \n \n {this.state.actionPaneMode && (\n {\n console.log(\"opening\");\n this.setState({ openSnackbar: true });\n }}\n sessionId={this.state.sessionId}\n onLocationSelect={this.handleLocationSelect}\n mapCenter={this.state.mapCenter}\n addedFeature={this.state.addedFeature}\n extMap={this.state.extMap}\n />\n )}\n {\n this.setState({ openSnackbar: false });\n }}\n >\n Upload gelukt! \n \n \n \n
\n {this.state.modalContent} \n \n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' },\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then(registration => {\n registration.unregister();\n })\n .catch(error => {\n console.error(error.message);\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(\n \n \n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n","module.exports = __webpack_public_path__ + \"static/media/image1.b31744a9.jpg\";","module.exports = __webpack_public_path__ + \"static/media/image2.ac889942.jpg\";","module.exports = __webpack_public_path__ + \"static/media/image10.0b22e969.jpg\";","module.exports = __webpack_public_path__ + \"static/media/image3.1567ca29.png\";","module.exports = __webpack_public_path__ + \"static/media/image4.1565f713.png\";","module.exports = __webpack_public_path__ + \"static/media/image6.ae2f7022.png\";","module.exports = __webpack_public_path__ + \"static/media/image7.b4e5b358.png\";","module.exports = __webpack_public_path__ + \"static/media/image8.eed47c64.png\";","module.exports = __webpack_public_path__ + \"static/media/image5.f653134c.png\";","module.exports = __webpack_public_path__ + \"static/media/image9.38d3aad6.png\";"],"sourceRoot":""}