Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions i18n/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -577,11 +577,18 @@ components:
linkCopied: Copied
reportIssue: Report Issue
reportEmailSubject: Reporting an Issue with OpenTripPlanner
reportEmailTemplate: >
***INSTRUCTIONS TO USER***
reportEmailTemplate: |+
*** INSTRUCTIONS TO USER ***
This feature allows you to email a report to site administrators for review.
Please add any additional feedback for this trip under the 'Additional Comments'
section below and send using your regular email program."
Please fill out the prompts below and send using your regular email program.

*** PLEASE COMPLETE THE FOLLOWING ***

Issue encountered:

Type of trip you wanted to take (ex. walk + transit, bike + transit, car + transit):

*** TECHNICAL DETAILS ***
# Used in both desktop and mobile
TripViewer:
accessible: Accessible
Expand Down
17 changes: 12 additions & 5 deletions i18n/fr-FR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -561,11 +561,18 @@ components:
linkCopied: Copié
reportIssue: Un problème ? # "Signaler un problème" does not fit.
reportEmailSubject: Signaler un problème avec OpenTripPlanner
reportEmailTemplate: >
*** À L'ATTENTION DE L'UTILISATEUR ***
Vous pouvez communiquer votre problème par e-mail et en détail aux administrateurs de ce site.
Veuillez ajouter toute remarque sur cet itinéraire dans la section 'Additional Comments'
ci-dessous, puis envoyez depuis votre logiciel de messagerie usuel."
reportEmailTemplate: |+
*** CONSIGNES POUR L'UTILISATEUR ***
Cet email signalera votre problème aux administrateurs de ce site.
Veuillez remplir les détails ci-dessous, puis envoyez depuis votre logiciel de messagerie habituel.

*** VEUILLEZ REMPLIR CI-DESSOUS ***

Problème rencontré :

Type de trajet recherché (ex. à pied + transports, vélo + transports, voiture + transports) :

*** DÉTAILS TECHNIQUES ***
# Used in both desktop and mobile
TripViewer:
accessible: Accessible
Expand Down
236 changes: 115 additions & 121 deletions lib/components/narrative/trip-tools.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,18 @@
/* eslint-disable no-case-declarations */
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'
import bowser from 'bowser'
import copyToClipboard from 'copy-to-clipboard'
import React, {Component} from 'react'
import { connect } from 'react-redux'
import { Button } from 'react-bootstrap'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
// import { DropdownButton, MenuItem } from 'react-bootstrap'
import { FormattedMessage, injectIntl } from 'react-intl'

import Icon from '../util/icon'

class TripTools extends Component {
static defaultProps = {
buttonTypes: [ 'COPY_URL', 'PRINT', 'REPORT_ISSUE', 'START_OVER' ]
}

render () {
const { buttonTypes, reactRouterConfig, reportConfig } = this.props

const buttonComponents = []
buttonTypes.forEach((type) => {
switch (type) {
case 'COPY_URL':
buttonComponents.push(<CopyUrlButton />)
break
case 'PRINT':
buttonComponents.push(<PrintButton />)
break
case 'REPORT_ISSUE':
if (!reportConfig || !reportConfig.mailto) break
buttonComponents.push(<ReportIssueButton {...reportConfig} />)
break
case 'START_OVER':
// Determine "home" URL
let startOverUrl = '/'
if (reactRouterConfig && reactRouterConfig.basename) {
startOverUrl += reactRouterConfig.basename
}
buttonComponents.push(
// FIXME: The Spanish string does not fit in button width.
<LinkButton
icon='undo'
text={<FormattedMessage id='common.forms.startOver' />}
url={startOverUrl}
/>
)
break
}
})

return (
<div className='trip-tools'>
{buttonComponents.map((btn, i) => <div className='button-container' key={i}>{btn}</div>)}
</div>
)
}
}

// Share/Save Dropdown Component -- not used currently

/*
class ShareSaveDropdownButton extends Component {
_onCopyToClipboardClick = () => {
copyToClipboard(window.location.href)
}

render () {
return (
<DropdownButton
className='tool-button'
title={<span><i className='fa fa-share' /> Share/Save</span>}
id={'tool-share-dropdown'}
>
<MenuItem onClick={this._onCopyToClipboardClick}>
<i className='fa fa-clipboard' /> Copy Link to Clipboard
</MenuItem>
</DropdownButton>
)
}
}
*/

// Copy URL Button

class CopyUrlButton extends Component {
constructor (props) {
constructor(props) {
super(props)
this.state = { showCopied: false }
}
Expand All @@ -100,35 +30,32 @@ class CopyUrlButton extends Component {
url = `${parts[0]}#/start/x/x/x/${routerId}${parts[1]}`
} else {
// Console logs are not internationalized.
console.warn('URL not formatted as expected, copied URL will not contain session routerId.', routerId)
console.warn(
'URL not formatted as expected, copied URL will not contain session routerId.',
routerId
)
}
}
copyToClipboard(url)
this.setState({ showCopied: true })
window.setTimeout(this._resetState, 2000)
}

render () {
render() {
return (
<div>
<Button
className='tool-button'
onClick={this._onClick}
>
{this.state.showCopied
? (
<span>
<Icon type='check' withSpace />
<FormattedMessage id='components.TripTools.linkCopied' />
</span>
)
: (
<span>
<Icon type='clipboard' withSpace />
<FormattedMessage id='components.TripTools.copyLink' />
</span>
)
}
<Button className="tool-button" onClick={this._onClick}>
{this.state.showCopied ? (
<span>
<Icon type="check" withSpace />
<FormattedMessage id="components.TripTools.linkCopied" />
</span>
) : (
<span>
<Icon type="clipboard" withSpace />
<FormattedMessage id="components.TripTools.copyLink" />
</span>
)}
</Button>
</div>
)
Expand All @@ -144,15 +71,12 @@ class PrintButton extends Component {
window.open(printUrl, '_blank')
}

render () {
render() {
return (
<div>
<Button
className='tool-button'
onClick={this._onClick}
>
<Icon type='print' withSpace />
<FormattedMessage id='common.forms.print' />
<Button className="tool-button" onClick={this._onClick}>
<Icon type="print" withSpace />
<FormattedMessage id="common.forms.print" />
</Button>
</div>
)
Expand All @@ -164,37 +88,45 @@ class PrintButton extends Component {
class ReportIssueButtonBase extends Component {
_onClick = () => {
const { intl, mailto, subject: configuredSubject } = this.props
const subject = configuredSubject || intl.formatMessage({id: 'components.TripTools.reportEmailSubject'})
const subject =
configuredSubject ||
intl.formatMessage({ id: 'components.TripTools.reportEmailSubject' })
const bodyLines = [
intl.formatMessage({id: 'components.TripTools.reportEmailTemplate'}),
intl.formatMessage({ id: 'components.TripTools.reportEmailTemplate' }),
'',
// Search data section is for support and is not translated.
'SEARCH DATA:',
'Address: ' + window.location.href,
'Browser: ' + bowser.name + ' ' + bowser.version,
'OS: ' + bowser.osname + ' ' + bowser.osversion,
'',
'ADDITIONAL COMMENTS:',
''
]

window.open(`mailto:${mailto}?subject=${subject}&body=${encodeURIComponent(bodyLines.join('\n'))}`, '_self')
window.open(
`mailto:${mailto}?subject=${subject}&body=${encodeURIComponent(
bodyLines.join('\n')
)}`,
'_self'
)
}

render () {
render() {
return (
<Button
className='tool-button'
onClick={this._onClick}
>
<Icon type='flag' withSpace />
<Button className="tool-button" onClick={this._onClick}>
<Icon type="flag" withSpace />
{/* FIXME: Depending on translation, Spanish and French strings may not fit in button width. */}
<FormattedMessage id='components.TripTools.reportIssue' />
<FormattedMessage id="components.TripTools.reportIssue" />
</Button>
)
}
}

ReportIssueButtonBase.propTypes = {
intl: PropTypes.object,
mailto: PropTypes.string,
subject: PropTypes.string
}

// The ReportIssueButton component above, with an intl prop
// for retrieving messages shown outside of React rendering.
const ReportIssueButton = injectIntl(ReportIssueButtonBase)
Expand All @@ -206,14 +138,11 @@ class LinkButton extends Component {
window.location.href = this.props.url
}

render () {
render() {
const { icon, text } = this.props
return (
<div>
<Button
className='tool-button'
onClick={this._onClick}
>
<Button className="tool-button" onClick={this._onClick}>
{icon && <Icon type={icon} withSpace />}
{text}
</Button>
Expand All @@ -222,9 +151,74 @@ class LinkButton extends Component {
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like it would work well as a functional component.

LinkButton.propTypes = {
icon: PropTypes.string,
text: PropTypes.string,
url: PropTypes.string
}

class TripTools extends Component {
static defaultProps = {
buttonTypes: ['COPY_URL', 'PRINT', 'REPORT_ISSUE', 'START_OVER']
}

render() {
const { buttonTypes, reactRouterConfig, reportConfig } = this.props

const buttonComponents = []
buttonTypes.forEach((type) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a map to map buttonTypes to buttonComponents?

switch (type) {
case 'COPY_URL':
buttonComponents.push(<CopyUrlButton />)
break
case 'PRINT':
buttonComponents.push(<PrintButton />)
break
case 'REPORT_ISSUE':
if (!reportConfig || !reportConfig.mailto) break
buttonComponents.push(<ReportIssueButton {...reportConfig} />)
break
case 'START_OVER':
// Determine "home" URL
let startOverUrl = '/'
if (reactRouterConfig && reactRouterConfig.basename) {
startOverUrl += reactRouterConfig.basename
}
buttonComponents.push(
// FIXME: The Spanish string does not fit in button width.
<LinkButton
icon="undo"
text={<FormattedMessage id="common.forms.startOver" />}
url={startOverUrl}
/>
)
break
default:
console.warn(`TripTools called with invalid button type ${type}!`)
}
})

return (
<div className="trip-tools">
{buttonComponents.map((btn, i) => (
<div className="button-container" key={i}>
{btn}
</div>
))}
</div>
)
}
}

TripTools.propTypes = {
buttonTypes: PropTypes.arrayOf(PropTypes.string),
reactRouterConfig: PropTypes.object,
reportConfig: PropTypes.object
}

// Connect main class to redux store

const mapStateToProps = (state, ownProps) => {
const mapStateToProps = (state) => {
return {
reactRouterConfig: state.otp.config.reactRouter,
reportConfig: state.otp.config.reportIssue
Expand Down