// Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React, {Component} from 'react'; import {polyfill} from 'react-lifecycles-compat'; import classnames from 'classnames'; import styled from 'styled-components'; import MapboxGLMap from 'react-map-gl'; import { StyledModalContent, InputLight, StyledMapContainer, StyledModalVerticalPanel, StyledModalSection } from 'components/common/styled-components'; import {media} from 'styles/media-breakpoints'; // Utils import {transformRequest} from 'utils/map-style-utils/mapbox-utils'; import {injectIntl, IntlShape} from 'react-intl'; import {FormattedMessage} from 'localization'; import {InputStyle, MapState} from 'reducers'; import mapboxgl from 'mapbox-gl'; const MapH = 190; const MapW = 264; const ErrorMsg = { styleError: 'Failed to load map style, make sure it is published. For private style, paste in your access token.' }; const PreviewMap = styled.div` align-items: center; display: flex; flex-direction: column; justify-content: center; margin-left: 116px; flex-shrink: 0; .preview-title { font-weight: 500; font-size: 10px; padding: 8px 0px; } .preview-title.error { color: ${props => props.theme.errorColor}; } ${media.portable` margin-left: 32px; `}; ${media.palm` margin-left: unset; .preview-title { margin-top: 0px; } `}; `; const StyledPreviewImage = styled.div` background: ${props => props.theme.modalImagePlaceHolder}; border-radius: 4px; box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.18); width: ${MapW}px; height: ${MapH}px; position: relative; .preview-image-placeholder { position: absolute; top: 0; left: 0; } .preview-image-spinner { position: absolute; left: calc(50% - 25px); top: calc(50% - 25px); } `; const InlineLink = styled.a` font-weight: 500; :hover { cursor: pointer; } `; interface AddMapStyleModalProps { inputMapStyle: Function; inputStyle: InputStyle; loadCustomMapStyle: Function; mapboxApiUrl?: string; mapState: MapState; intl: IntlShape; } function AddMapStyleModalFactory() { class AddMapStyleModal extends Component { state = { reRenderKey: 0, previousToken: null }; static getDerivedStateFromProps(props, state) { if ( props.inputStyle && props.inputStyle.accessToken && props.inputStyle.accessToken !== state.previousToken ) { // toke has changed // ReactMapGl doesn't re-create map when token has changed // here we force the map to update return { reRenderKey: state.reRenderKey + 1, previousToken: props.inputStyle.accessToken }; } return null; } // @ts-expect-error mapRef: MapboxGLMap | null | undefined; _map: mapboxgl.Map | undefined; componentDidUpdate() { const map = this.mapRef && this.mapRef.getMap(); if (map && this._map !== map) { this._map = map; map.on('style.load', () => { const style = map.getStyle(); this.loadMapStyleJson(style); }); map.on('error', () => { this.loadMapStyleError(); }); } } loadMapStyleJson = style => { this.props.loadCustomMapStyle({style, error: false}); }; loadMapStyleError = () => { this.props.loadCustomMapStyle({error: true}); }; render() { const {inputStyle, mapState, mapboxApiUrl, intl} = this.props; const mapProps = { ...mapState, mapboxApiUrl, preserveDrawingBuffer: true, transformRequest }; return (
{intl.formatMessage({id: 'modal.addStyle.pasteSubtitle0'})} {' '} {intl.formatMessage({id: 'modal.addStyle.pasteSubtitle2'})} {' '} {intl.formatMessage({id: 'modal.addStyle.pasteSubtitle3'})} {' '} {intl.formatMessage({id: 'modal.addStyle.pasteSubtitle4'})}
this.props.inputMapStyle({url: value})} placeholder="e.g. mapbox://styles/username/style, http://my.stles.com/xxx/style.json " />
{intl.formatMessage({id: 'modal.addStyle.publishSubtitle1'})} {' '} mapbox {' '} {intl.formatMessage({id: 'modal.addStyle.publishSubtitle2'})} {' '} {intl.formatMessage({id: 'modal.addStyle.publishSubtitle3'})} {' '} {intl.formatMessage({id: 'modal.addStyle.publishSubtitle4'})}
{intl.formatMessage({id: 'modal.addStyle.publishSubtitle5'})} {' '} {intl.formatMessage({id: 'modal.addStyle.publishSubtitle6'})} {' '} {intl.formatMessage({id: 'modal.addStyle.publishSubtitle7'})}
this.props.inputMapStyle({accessToken: value})} placeholder={intl.formatMessage({id: 'modal.addStyle.exampleToken'})} />
this.props.inputMapStyle({label: value})} />
{inputStyle.error ? ErrorMsg.styleError : (inputStyle.style && inputStyle.style.name) || ''}
{!inputStyle.isValid ? (
) : ( { this.mapRef = el; }} key={this.state.reRenderKey} width={MapW} height={MapH} mapStyle={inputStyle.url === null ? undefined : inputStyle.url} /> )}
); } } return injectIntl(polyfill(AddMapStyleModal)); } export default AddMapStyleModalFactory;