import React from "react" import { StyleSheet, View, Image, ScrollView, Text, TextInput, KeyboardAvoidingView, Platform, TouchableOpacity, } from "react-native" import MapView, { Marker } from "react-native-maps" import { FormBinder } from "react-form-binder" import { BoundInput, BoundButton, BoundHeader, Icon, Header, BoundOptionStrip, BoundPhotoPanel, Geolocation, } from "../ui" import { MessageModal, ProgressModal, WaitModal } from "../Modal" import autobind from "autobind-decorator" import { ifIphoneX, isIphoneX } from "react-native-iphone-x-helper" import KeyboardSpacer from "react-native-keyboard-spacer" import { api } from "../API" import "url-search-params-polyfill" import { config } from "../config" import { workItemTypeEnum, formatLatLng, parseLatLng } from "../util" import PropTypes from "prop-types" const styles = StyleSheet.create({ container: { flexDirection: "column", flexGrow: 1, backgroundColor: "#DDDDDD", }, panel: { width: "94%", backgroundColor: "white", alignSelf: "center", marginTop: "3%", shadowColor: "gray", shadowOffset: { width: 2, height: 2 }, shadowRadius: 2, shadowOpacity: 0.5, padding: 10, }, }) export class WorkItem extends React.Component { static propTypes = { history: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), } static bindings = { header: { noValue: true, isDisabled: (r) => !(r.anyModified && r.allValid), }, location: { isValid: (r, v) => v !== "", isReadOnly: true, alwaysGet: true, initValue: null, pre: (v) => v !== null ? formatLatLng(v.coordinates[1], v.coordinates[0]) : "", post: (v) => parseLatLng(v), }, address: { isValid: true, isReadOnly: true, }, photos: { initValue: [], isValid: (r, v) => v && v.length > 0, }, details: { isValid: (r, v) => v !== "", }, workItemType: { isValid: (r, v) => v !== "", alwaysGet: true, }, addActivity: { noValue: true, isDisabled: (r, v) => r.anyModified, }, } constructor(props) { super(props) this.state = { binder: new FormBinder({}, WorkItem.bindings), messageModal: null, waitModal: null, progressModal: null, region: config.initialRegion, } const { search } = this.props.location const params = search ? new URLSearchParams(search) : { get: () => null } const id = params.get("id") if (id) { api .getWorkItem(id) .then((workItem) => { if (workItem) { const [longitude, latitude] = workItem.location.coordinates const region = { latitude, longitude, latitudeDelta: 0.01, longitudeDelta: 0.01, } if (this.mapView) { this.mapView.animateToRegion(region) } else { this.goToRegion = region } this.setState({ binder: new FormBinder(workItem, WorkItem.bindings), }) } }) .catch((err) => { this.setState({ messageModal: { icon: "hand", message: "Unable to get work item details", detail: err.message, back: true, }, }) }) } } componentWillUnmount() { if (this.geoCodeTimer) { clearTimeout(this.geoCodeTimer) } } @autobind handleBackPress() { const { history } = this.props if (history.length > 1) { history.goBack() } else { history.replace("/home") } } @autobind handleDonePress() { const { binder } = this.state let obj = binder.getModifiedBindingValues() if (!obj._id) { api .createWorkItem(obj) .then((workItem) => { this.handleBackPress() }) .catch((error) => { this.setState({ messageModal: { icon: "hand", message: "Unable to create work item", detail: error.message, }, }) }) } else { api .updateWorkItem(obj) .then((workItem) => { this.handleBackPress() }) .catch((error) => { this.setState({ messageModal: { icon: "hand", message: "Unable to update work item", detail: error.message, }, }) }) } } @autobind handleMessageDismiss() { const back = this.state.messageModal.back this.setState({ messageModal: null }) if (back) { this.handleBackPress() } } @autobind handleRegionChange(region) { const { latitude, longitude } = region if (this.latLngInput) { this.latLngInput.handleChangeText(formatLatLng(latitude, longitude)) } if (this.geoCodeTimer) { clearTimeout(this.geoCodeTimer) this.geoCodeTimer = null } this.geoCodeTimer = setTimeout( () => this.handleStartAddressLookup({ latitude, longitude }), config.geocodeDelayMilliseconds ) } @autobind handleOnMapReady() { if (this.goToRegion && this.mapView) { this.mapView.animateToRegion(this.goToRegion) this.goToRegion = null } } @autobind handleStartAddressLookup(latLng) { api .getAddress(latLng) .then((address) => { if (this.addressInput) { this.addressInput.handleChangeText(address) } }) .catch(() => { if (this.addressInput) { this.addressInput.handleChangeText("") } }) } @autobind handleUploadStarted() { this.setState({ progressModal: { message: "Uploading Photo..." }, uploadPercent: 0, }) } @autobind handleUploadProgress(uploadData) { if (this.state.progressModal) { this.setState({ uploadPercent: Math.round( uploadData.uploadedChunks / uploadData.numberOfChunks * 100 ), }) return true } else { return false } } @autobind handleUploadEnded(successful, uploadData) { this.setState({ progressModal: null }) } @autobind handleUploadCanceled() { this.setState({ progressModal: null }) } @autobind handleAddActivity() { if (this.props.history) { this.props.history.push(`/activity?workItemId=${this.state.binder._id}`) } } @autobind handlePositionUpdate(position) { const { coords } = position this.setState({ region: { latitude: coords.latitude, longitude: coords.longitude, latitudeDelta: 0.02, longitudeDelta: 0.02, }, }) } render() { const { binder, messageModal, waitModal, progressModal, uploadPercent, region, } = this.state return ( (this.mapView = ref)} style={{ flexDirection: "column", justifyContent: "center", width: "100%", height: 400, marginBottom: 10, }} zoomControlEnabled={false} zoomEnabled={!binder._id} scrollEnabled={!binder._id} rotateEnabled={false} pitchEnabled={false} showsUserLocation showsBuildings={false} showsTraffic={false} showsIndoors={false} region={region} onRegionChange={this.handleRegionChange} onMapReady={this.handleOnMapReady} /> (this.latLngInput = ref)} binder={binder} name="location" label="Location:" /> (this.addressInput = ref)} binder={binder} name="address" label="Address:" /> {api.loggedInUser.administrator && binder._id && ( )} {isIphoneX ? : null} {Platform.OS === "ios" && } ) } }