From 57bb8b1f04cdb925321dcc6b921b8249264b84fb Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 11 May 2023 16:38:01 -0400 Subject: [PATCH 01/49] refactor(NotificationPrefsPane): Remove redundant header --- i18n/en-US.yml | 1 - i18n/es.yml | 30 +++++++------------ i18n/fr.yml | 3 -- i18n/ko.yml | 1 - i18n/vi.yml | 3 -- i18n/zh.yml | 1 - .../user/notification-prefs-pane.tsx | 3 -- 7 files changed, 10 insertions(+), 32 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index b2e036689..e8b46ea69 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -356,7 +356,6 @@ components: description: The content you requested is not available. header: Content not found NotificationPrefsPane: - description: You can receive notifications about trips you frequently take. noneSelect: Don't notify me notificationChannelPrompt: How would you like to receive notifications? notificationEmailDetail: "Notification emails will be sent to:" diff --git a/i18n/es.yml b/i18n/es.yml index 08c82b945..29a962b6d 100644 --- a/i18n/es.yml +++ b/i18n/es.yml @@ -30,8 +30,7 @@ actions: No se puede guardar el plan: este plan no se pudo guardar debido a la falta de capacidad en uno o más vehículos. Por favor, vuelva a planificar su viaje. - maxTripRequestsExceeded: Número de solicitudes de viaje superadas sin resultados - válidos + maxTripRequestsExceeded: Número de solicitudes de viaje superadas sin resultados válidos saveItinerariesError: "No se pudieron guardar los itinerarios: {err}" setDateError: "Error al establecer la fecha:" setGroupSizeError: "No se pudo establecer el tamaño del grupo:" @@ -47,11 +46,9 @@ actions: authTokenError: Error al obtener un token de autorización. confirmDeleteMonitoredTrip: ¿Desea eliminar este viaje? confirmDeletePlace: ¿Quiere eliminar este lugar? - emailVerificationResent: El mensaje de verificación de correo electrónico ha sido - reenviado. + emailVerificationResent: El mensaje de verificación de correo electrónico ha sido reenviado. genericError: "Se ha encontrado un error: {err}" - itineraryExistenceCheckFailed: Comprobación de errores para ver si el viaje seleccionado - es posible. + itineraryExistenceCheckFailed: Comprobación de errores para ver si el viaje seleccionado es posible. preferencesSaved: Sus preferencias se han guardado. smsInvalidCode: El código introducido no es válido. Por favor, inténtelo de nuevo. smsResendThrottled: >- @@ -113,6 +110,7 @@ common: {co2} de CO₂ en {isMore, select, true {más} other {menos} } que conducir solo transfers: "{transfers, plural, =0 {} one {# transferencia} other {# transferencias}}" + linkOpensNewWindow: (Abre una nueva ventana) modes: bicycle_rent: Compartir bicicleta bike: Bicicleta @@ -160,7 +158,6 @@ common: tripDurationFormat: >- {hours, plural, =0 {} other {# h }}{minutes} min { seconds, plural, =0 {} other {# s}} - linkOpensNewWindow: (Abre una nueva ventana) components: A11yPrefs: accessibilityRoutingByDefault: Prefiera los viajes accesibles por defecto @@ -237,8 +234,7 @@ components: origin: origen planTripTooltip: Planificar viaje settings: Preferencias de viaje - validationMessage: "Por favor, defina los siguientes campos para planificar un\ - \ viaje: {issues}" + validationMessage: "Por favor, defina los siguientes campos para planificar un viaje: {issues}" BeforeSignInScreen: mainTitle: Iniciando sesión message: > @@ -366,11 +362,9 @@ components: description: El contenido que ha solicitado no está disponible. header: No se encontró el contenido NotificationPrefsPane: - description: Puede recibir notificaciones sobre los viajes que realiza con frecuencia. noneSelect: No enviar notificaciones notificationChannelPrompt: ¿Cómo desea recibir las notificaciones? - notificationEmailDetail: "Los correos electrónicos de notificación se enviarán\ - \ a:" + notificationEmailDetail: "Los correos electrónicos de notificación se enviarán a:" PhoneNumberEditor: changeNumber: Cambiar número de teléfono invalidCode: Introduzca 6 dígitos para el código de validación. @@ -503,8 +497,7 @@ components: Opciones y Preferencias del viaje SimpleRealtimeAnnotation: - usingRealtimeInfo: Este viaje utiliza información de tráfico y retrasos en tiempo - real + usingRealtimeInfo: Este viaje utiliza información de tráfico y retrasos en tiempo real StackedPaneDisplay: savePreferences: Guardar preferencias StopScheduleTable: @@ -566,19 +559,16 @@ components: travelingAt: Viajando a {milesPerHour} vehicleName: Vehículo {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Comprobación de la existencia de itinerarios para - cada día de la semana… + checkingItineraryExistence: Comprobación de la existencia de itinerarios para cada día de la semana… selectAtLeastOneDay: Por favor, seleccione al menos un día para el seguimiento. tripDaysPrompt: ¿Qué días hace este viaje? - tripIsAvailableOnDaysIndicated: Su viaje está disponible en los días de la semana - indicados anteriormente. + tripIsAvailableOnDaysIndicated: Su viaje está disponible en los días de la semana indicados anteriormente. tripNamePrompt: "Por favor, indique un nombre para este viaje:" tripNotAvailableOnDay: El viaje no está disponible el {repeatedDay} unsavedChangesExistingTrip: >- Todavía no ha guardado su viaje. Si abandona la página, los cambios se perderán. - unsavedChangesNewTrip: Todavía no ha guardado su nuevo viaje. Si abandona la página, - se perderá. + unsavedChangesNewTrip: Todavía no ha guardado su nuevo viaje. Si abandona la página, se perderá. TripNotificationsPane: advancedSettings: Configuración avanzada altRouteRecommended: Se recomienda una ruta alternativa o un punto de transferencia diff --git a/i18n/fr.yml b/i18n/fr.yml index 925933b69..29c3ee110 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -368,9 +368,6 @@ components: description: Le contenu que vous avez demandé n'est pas disponible. header: Contenu introuvable NotificationPrefsPane: - description: >- - Vous pouvez recevoir des notifications sur les trajets que vous effectuez - fréquemment. noneSelect: Ne pas me notifier notificationChannelPrompt: Comment voulez-vous recevoir vos notifications ? notificationEmailDetail: "Les courriers de notification seront envoyés à :" diff --git a/i18n/ko.yml b/i18n/ko.yml index a4d9f9afa..605c75f7a 100644 --- a/i18n/ko.yml +++ b/i18n/ko.yml @@ -335,7 +335,6 @@ components: description: 요청한 콘텐츠를 사용할 수 없습니다. header: 콘텐츠를 찾을 수 없음 NotificationPrefsPane: - description: 자주 가는 트립에 대한 알림을 받을 수 있습니다. noneSelect: 알림 거부 notificationChannelPrompt: 알림을 어떻게 받고 싶습니까? notificationEmailDetail: "알림 이메일이 다음으로 전송됩니다:" diff --git a/i18n/vi.yml b/i18n/vi.yml index 854e37905..64f48d2d3 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -343,9 +343,6 @@ components: description: Nội dung bạn yêu cầu không có sẵn. header: Không tìm thấy nội dung NotificationPrefsPane: - description: >- - Bạn có thể nhận được thông báo về các chuyến đi bạn thường xuyên thực - hiện. noneSelect: Đừng thông báo cho tôi notificationChannelPrompt: Bạn muốn nhận thông báo như thế nào? notificationEmailDetail: "Email thông báo sẽ được gửi đến:" diff --git a/i18n/zh.yml b/i18n/zh.yml index e44e03592..bc32777c4 100644 --- a/i18n/zh.yml +++ b/i18n/zh.yml @@ -335,7 +335,6 @@ components: description: 您要求的内容不存在. header: 未找到内容 NotificationPrefsPane: - description: 你可以收到关于你常用行程的通知. noneSelect: 不要通知我 notificationChannelPrompt: 您希望如何接收通知? notificationEmailDetail: "通知邮件将被发送至:" diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 4bee1cca9..ac9ea0ca8 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -53,9 +53,6 @@ const NotificationPrefsPane = ({ return (
-

- -

From 18aa10220ff8dbbf1a3fa1381f5a39cbcb7e7e7b Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 11 May 2023 16:56:11 -0400 Subject: [PATCH 02/49] refactor(NotificationPrefsPane): Rephrase the notification selector prompt. --- i18n/en-US.yml | 2 +- i18n/es.yml | 2 +- i18n/fr.yml | 2 +- i18n/ko.yml | 2 +- i18n/vi.yml | 2 +- i18n/zh.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index e8b46ea69..e13f71b32 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -357,7 +357,7 @@ components: header: Content not found NotificationPrefsPane: noneSelect: Don't notify me - notificationChannelPrompt: How would you like to receive notifications? + notificationChannelPrompt: "Receive notifications about your saved trips via:" notificationEmailDetail: "Notification emails will be sent to:" PhoneNumberEditor: changeNumber: Change number diff --git a/i18n/es.yml b/i18n/es.yml index 29a962b6d..e6142f4fc 100644 --- a/i18n/es.yml +++ b/i18n/es.yml @@ -363,7 +363,7 @@ components: header: No se encontró el contenido NotificationPrefsPane: noneSelect: No enviar notificaciones - notificationChannelPrompt: ¿Cómo desea recibir las notificaciones? + notificationChannelPrompt: "Recibir notificaciones para sus viajes guardados por :" notificationEmailDetail: "Los correos electrónicos de notificación se enviarán a:" PhoneNumberEditor: changeNumber: Cambiar número de teléfono diff --git a/i18n/fr.yml b/i18n/fr.yml index 29c3ee110..4133097ed 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -369,7 +369,7 @@ components: header: Contenu introuvable NotificationPrefsPane: noneSelect: Ne pas me notifier - notificationChannelPrompt: Comment voulez-vous recevoir vos notifications ? + notificationChannelPrompt: "Recevoir des notifications sur vos trajets enregistrés par :" notificationEmailDetail: "Les courriers de notification seront envoyés à :" PhoneNumberEditor: changeNumber: Changer de numéro diff --git a/i18n/ko.yml b/i18n/ko.yml index 605c75f7a..71434375c 100644 --- a/i18n/ko.yml +++ b/i18n/ko.yml @@ -336,7 +336,7 @@ components: header: 콘텐츠를 찾을 수 없음 NotificationPrefsPane: noneSelect: 알림 거부 - notificationChannelPrompt: 알림을 어떻게 받고 싶습니까? + notificationChannelPrompt: "저장된 여행의 알림을 받는 방법:" notificationEmailDetail: "알림 이메일이 다음으로 전송됩니다:" PhoneNumberEditor: changeNumber: 번호 변경 diff --git a/i18n/vi.yml b/i18n/vi.yml index 64f48d2d3..694873eb5 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -344,7 +344,7 @@ components: header: Không tìm thấy nội dung NotificationPrefsPane: noneSelect: Đừng thông báo cho tôi - notificationChannelPrompt: Bạn muốn nhận thông báo như thế nào? + notificationChannelPrompt: "Nhận thông báo về các chuyến đi đã lưu bằng:" notificationEmailDetail: "Email thông báo sẽ được gửi đến:" PhoneNumberEditor: changeNumber: Thay đổi số điện thoại diff --git a/i18n/zh.yml b/i18n/zh.yml index bc32777c4..c83ce0d63 100644 --- a/i18n/zh.yml +++ b/i18n/zh.yml @@ -336,7 +336,7 @@ components: header: 未找到内容 NotificationPrefsPane: noneSelect: 不要通知我 - notificationChannelPrompt: 您希望如何接收通知? + notificationChannelPrompt: "如何接收已保存行程的通知:" notificationEmailDetail: "通知邮件将被发送至:" PhoneNumberEditor: changeNumber: 更改电话号码 From 148190bb733d66597b139701b568b9c3b1531b66 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 11 May 2023 18:18:15 -0400 Subject: [PATCH 03/49] refactor(NotificationPrefsPane): Update layout to use checkboxes (rough) --- .../user/notification-prefs-pane.tsx | 99 +++++++++++-------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index ac9ea0ca8..38dc4713c 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -29,7 +29,7 @@ interface Props extends FormikProps { } } -const allowedNotificationChannels = ['email', 'sms', 'none'] +const allowedNotificationChannels = ['email', 'sms'] // Styles // HACK: Preserve container height. @@ -38,6 +38,22 @@ const Details = styled.div` margin-bottom: 15px; ` +const NotificationOption = styled.div` + align-items: flex-start; + display: flex; + gap: 1ch; + margin-bottom: 10px; + + label { + display: block; + font-weight: normal; + margin-bottom: 0; + } + label::first-letter { + text-transform: uppercase; + } +` + /** * User notification preferences pane. */ @@ -53,44 +69,49 @@ const NotificationPrefsPane = ({ return (
- - - - - - {allowedNotificationChannels.map((type) => { - // TODO: If removing the Save/Cancel buttons on the account screen, - // persist changes immediately when onChange is triggered. - const inputId = `notification-channel-${type}` - const isChecked = notificationChannel === type - return ( - - {/* Note: labels are placed after inputs so that the CSS focus selector can be easily applied. */} - - - - ) - })} - - +
+ + + + {allowedNotificationChannels.map((type) => { + // TODO: If removing the Save/Cancel buttons on the account screen, + // persist changes immediately when onChange is triggered. + const inputId = `notification-channel-${type}` + return ( + + {' '} + + {type === 'email' ? ( + <> + + {email} + + ) : ( + <> + + + + )} + + + ) + })} +
{notificationChannel === 'email' && ( From 5d04a3953567421558c8e9d6a52a0c1e20d2f66f Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 12 May 2023 11:00:09 -0400 Subject: [PATCH 04/49] refactor(NotificationPrefsPane): Improve layout for phone number description --- i18n/en-US.yml | 1 - i18n/es.yml | 1 - i18n/fr.yml | 1 - i18n/ko.yml | 1 - i18n/vi.yml | 1 - i18n/zh.yml | 1 - .../user/notification-prefs-pane.tsx | 51 +++++--------- lib/components/user/phone-number-editor.tsx | 66 +++++++++---------- 8 files changed, 49 insertions(+), 74 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index e13f71b32..959eb6980 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -370,7 +370,6 @@ components: prompt: "Enter your phone number for SMS notifications:" requestNewCode: Request a new code sendVerificationText: Send verification text - smsDetail: "SMS notifications will be sent to:" verificationCode: "Verification code:" verificationInstructions: > Please check the SMS messaging app on your mobile phone for a text message diff --git a/i18n/es.yml b/i18n/es.yml index e6142f4fc..9cfc2d409 100644 --- a/i18n/es.yml +++ b/i18n/es.yml @@ -378,7 +378,6 @@ components: texto: requestNewCode: Solicitar un nuevo código sendVerificationText: Enviar texto de verificación - smsDetail: "Las notificaciones por mensaje de texto se enviarán a:" verificationCode: "Código de verificación:" verificationInstructions: > Por favor, compruebe en la aplicación de mensajería de texto de su diff --git a/i18n/fr.yml b/i18n/fr.yml index 4133097ed..e3bfde8e1 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -382,7 +382,6 @@ components: prompt: "Entrez votre numéro de téléphone pour les SMS de notification :" requestNewCode: Envoyer un nouveau code sendVerificationText: Envoyer le SMS de vérification - smsDetail: "Les SMS de notification seront envoyés au :" verificationCode: "Code de vérification :" verificationInstructions: > Un SMS vous a été envoyé avec un code de vérification. Veuillez taper ce diff --git a/i18n/ko.yml b/i18n/ko.yml index 71434375c..627b4a9de 100644 --- a/i18n/ko.yml +++ b/i18n/ko.yml @@ -348,7 +348,6 @@ components: prompt: "SMS 알림 수신을 위한 전화번호를 입력하세요:" requestNewCode: 새 코드 요청 sendVerificationText: 확인 텍스트 전송 - smsDetail: "SMS 알림이 다음으로 전송됩니다:" verificationCode: "확인 코드:" verificationInstructions: | 휴대폰의 SMS 메시지 앱에서 인증 코드를 확인하고 아래에 코드를 입력하세요(코드는 10분 후에 만료됩니다). diff --git a/i18n/vi.yml b/i18n/vi.yml index 694873eb5..7b582ca5c 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -356,7 +356,6 @@ components: prompt: "Nhập số điện thoại của bạn để nhận thông báo SMS:" requestNewCode: Yêu cầu một mã mới sendVerificationText: Gửi văn bản xác minh - smsDetail: "Thông báo SMS sẽ được gửi đến:" verificationCode: "Mã xác nhận:" verificationInstructions: > Vui lòng kiểm tra ứng dụng nhắn tin SMS trên điện thoại di động của bạn để diff --git a/i18n/zh.yml b/i18n/zh.yml index c83ce0d63..59f0f3fec 100644 --- a/i18n/zh.yml +++ b/i18n/zh.yml @@ -348,7 +348,6 @@ components: prompt: "输入你的电话号码以便收到短信通知:" requestNewCode: 申请一个新的代码 sendVerificationText: 发送验证短信 - smsDetail: "短信通知将被发送到:" verificationCode: "验证码:" verificationInstructions: | 请检查您手机上的短信应用查看是否有验证码的短信并输入以下代码 (代码在10分钟后失效). diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 38dc4713c..d929c512a 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -1,12 +1,9 @@ import { Field, FormikProps } from 'formik' import { FormattedMessage } from 'react-intl' -import { FormGroup } from 'react-bootstrap' import React, { Fragment } from 'react' import styled from 'styled-components' -import ButtonGroup from '../util/button-group' - -import { FakeLabel, InlineStatic } from './styled' +import { labelStyle } from './styled' import { PhoneVerificationSubmitHandler } from './phone-verification-form' import PhoneNumberEditor, { PhoneCodeRequestHandler @@ -32,12 +29,6 @@ interface Props extends FormikProps { const allowedNotificationChannels = ['email', 'sms'] // Styles -// HACK: Preserve container height. -const Details = styled.div` - min-height: 60px; - margin-bottom: 15px; -` - const NotificationOption = styled.div` align-items: flex-start; display: flex; @@ -54,6 +45,13 @@ const NotificationOption = styled.div` } ` +const NotificationOptions = styled.fieldset` + /* Format like labels. */ + legend { + ${labelStyle} + } +` + /** * User notification preferences pane. */ @@ -62,14 +60,13 @@ const NotificationPrefsPane = ({ onRequestPhoneVerificationCode, onSendPhoneVerificationCode, phoneFormatOptions, - values: userData // Formik prop + values: userData // Formik prop // TODO: remove }: Props): JSX.Element => { const { email, isPhoneNumberVerified, phoneNumber } = loggedInUser - const { notificationChannel } = userData return (
-
+ @@ -77,9 +74,11 @@ const NotificationPrefsPane = ({ // TODO: If removing the Save/Cancel buttons on the account screen, // persist changes immediately when onChange is triggered. const inputId = `notification-channel-${type}` + const inputDescriptionId = `${inputId}-description` return ( - {email} + + {email} + ) : ( <> @@ -99,6 +100,7 @@ const NotificationPrefsPane = ({ ) })} -
-
- {notificationChannel === 'email' && ( - - - - - {email} - - )} - {notificationChannel === 'sms' && ( - - )} -
+
) } diff --git a/lib/components/user/phone-number-editor.tsx b/lib/components/user/phone-number-editor.tsx index 7e9113a32..4fef908f4 100644 --- a/lib/components/user/phone-number-editor.tsx +++ b/lib/components/user/phone-number-editor.tsx @@ -10,7 +10,7 @@ import { isBlank } from '../../util/ui' import InvisibleA11yLabel from '../util/invisible-a11y-label' import SpanWithSpace from '../util/span-with-space' -import { ControlStrip, FakeLabel, InlineStatic } from './styled' +import { ControlStrip } from './styled' import PhoneChangeForm, { PhoneChangeSubmitHandler } from './phone-change-form' import PhoneVerificationForm, { PhoneVerificationSubmitHandler @@ -19,7 +19,7 @@ import PhoneVerificationForm, { export type PhoneCodeRequestHandler = (phoneNumber: string) => void const PlainLink = styled(SpanWithSpace)` - color: inherit; + color: #757575; &:hover { text-decoration: none; } @@ -34,6 +34,7 @@ const blankState = { } interface Props { + descriptorId: string initialPhoneNumber?: string initialPhoneNumberVerified?: boolean intl: IntlShape @@ -169,7 +170,7 @@ class PhoneNumberEditor extends Component { } render() { - const { initialPhoneNumber, phoneFormatOptions } = this.props + const { descriptorId, initialPhoneNumber, phoneFormatOptions } = this.props const { isEditing, phoneNumberReceived, @@ -220,9 +221,6 @@ class PhoneNumberEditor extends Component { return ( <> - - {ariaAlertContent} - {isEditing ? ( { /> ) : ( - - - - - - {shownPhoneNumber} - - {/* Invisible parentheses for no-CSS and screen readers */} - ( - {isPending ? ( - - - - ) : ( - - - - )} - ) - + + {shownPhoneNumber} + + {/* Invisible parentheses for no-CSS and screen readers */} + ( + {isPending ? ( + + + + ) : ( + + + + )} + ) )} + + {ariaAlertContent} + {isPending && !isEditing && ( Date: Fri, 12 May 2023 12:00:52 -0400 Subject: [PATCH 05/49] refactor(NotificationPrefsPane): Improve layout. --- .../user/notification-prefs-pane.tsx | 23 ++++++++++++------- lib/components/user/phone-number-editor.tsx | 10 +++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index d929c512a..0e72a4db5 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -32,9 +32,14 @@ const allowedNotificationChannels = ['email', 'sms'] const NotificationOption = styled.div` align-items: flex-start; display: flex; - gap: 1ch; margin-bottom: 10px; + /* Use bootstrap's spacing between checkbox and label */ + & > span:first-child { + flex-shrink: 0; + width: 20px; + } + label { display: block; font-weight: normal; @@ -77,13 +82,15 @@ const NotificationPrefsPane = ({ const inputDescriptionId = `${inputId}-description` return ( - {' '} + + + {type === 'email' ? ( <> diff --git a/lib/components/user/phone-number-editor.tsx b/lib/components/user/phone-number-editor.tsx index 4fef908f4..62f9c33e4 100644 --- a/lib/components/user/phone-number-editor.tsx +++ b/lib/components/user/phone-number-editor.tsx @@ -8,7 +8,6 @@ import styled from 'styled-components' import { getAriaPhoneNumber } from '../../util/a11y' import { isBlank } from '../../util/ui' import InvisibleA11yLabel from '../util/invisible-a11y-label' -import SpanWithSpace from '../util/span-with-space' import { ControlStrip } from './styled' import PhoneChangeForm, { PhoneChangeSubmitHandler } from './phone-change-form' @@ -18,7 +17,7 @@ import PhoneVerificationForm, { export type PhoneCodeRequestHandler = (phoneNumber: string) => void -const PlainLink = styled(SpanWithSpace)` +const PlainLink = styled.a` color: #757575; &:hover { text-decoration: none; @@ -232,15 +231,12 @@ class PhoneNumberEditor extends Component { ) : ( + {/* Use an anchor so that the aria-label applies and phone actions can be performed, + if necessary. Styling will make the text appear plain (mostly). */} {shownPhoneNumber} From 08fee5bcf3db667ffe92626df52f1f4f090b3791 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 12 May 2023 12:33:14 -0400 Subject: [PATCH 06/49] refactor: Refactor fieldset and color styles. --- .../user/monitored-trip/trip-basics-pane.tsx | 9 ++------ .../trip-notifications-pane.tsx | 15 +++---------- .../user/notification-prefs-pane.tsx | 22 ++++++++----------- lib/components/user/phone-number-editor.tsx | 3 ++- lib/components/user/styled.ts | 15 ++++++------- lib/components/util/button-group.tsx | 11 ++-------- 6 files changed, 25 insertions(+), 50 deletions(-) diff --git a/lib/components/user/monitored-trip/trip-basics-pane.tsx b/lib/components/user/monitored-trip/trip-basics-pane.tsx index 3ab97bf9e..b2aa897cc 100644 --- a/lib/components/user/monitored-trip/trip-basics-pane.tsx +++ b/lib/components/user/monitored-trip/trip-basics-pane.tsx @@ -18,9 +18,9 @@ import styled from 'styled-components' import type { IntlShape, WrappedComponentProps } from 'react-intl' import * as userActions from '../../../actions/user' +import { FieldSet } from '../styled' import { getErrorStates } from '../../../util/ui' import { getFormattedDayOfWeekPlural } from '../../../util/monitored-trip' -import { labelStyle } from '../styled' import FormattedDayOfWeek from '../../util/formatted-day-of-week' import FormattedDayOfWeekCompact from '../../util/formatted-day-of-week-compact' import FormattedValidationError from '../../util/formatted-validation-error' @@ -65,12 +65,7 @@ const ALL_DAYS = [ ] as const // Styles. -const AvailableDays = styled.fieldset` - /* Format like labels. */ - legend { - ${labelStyle} - } - +const AvailableDays = styled(FieldSet)` & > span { border: 1px solid #ccc; border-left: none; diff --git a/lib/components/user/monitored-trip/trip-notifications-pane.tsx b/lib/components/user/monitored-trip/trip-notifications-pane.tsx index a6702f08a..1428b8c22 100644 --- a/lib/components/user/monitored-trip/trip-notifications-pane.tsx +++ b/lib/components/user/monitored-trip/trip-notifications-pane.tsx @@ -5,6 +5,7 @@ import { FormattedMessage, IntlShape, useIntl } from 'react-intl' import React, { Component, ComponentType, FormEvent, ReactNode } from 'react' import styled from 'styled-components' +import { FieldSet } from '../styled' import { IconWithText } from '../../util/styledIcon' // Element styles @@ -36,16 +37,6 @@ const Summary = styled.summary` margin-bottom: 5px; ` -const NotificationSettings = styled.fieldset` - /* Format like labels. */ - legend { - border: none; - font-size: inherit; - font-weight: 700; - margin-bottom: 5px; - } -` - /** * A label followed by a dropdown control. */ @@ -214,7 +205,7 @@ class TripNotificationsPane extends Component { ) } else { notificationSettingsContent = ( - +
{
- + ) } diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 0e72a4db5..5d95db2d2 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -3,7 +3,9 @@ import { FormattedMessage } from 'react-intl' import React, { Fragment } from 'react' import styled from 'styled-components' -import { labelStyle } from './styled' +import { GRAY_ON_WHITE } from '../util/colors' + +import { FieldSet } from './styled' import { PhoneVerificationSubmitHandler } from './phone-verification-form' import PhoneNumberEditor, { PhoneCodeRequestHandler @@ -34,7 +36,7 @@ const NotificationOption = styled.div` display: flex; margin-bottom: 10px; - /* Use bootstrap's spacing between checkbox and label */ + /* Match bootstrap's spacing between checkbox and label */ & > span:first-child { flex-shrink: 0; width: 20px; @@ -48,12 +50,8 @@ const NotificationOption = styled.div` label::first-letter { text-transform: uppercase; } -` - -const NotificationOptions = styled.fieldset` - /* Format like labels. */ - legend { - ${labelStyle} + label + span { + color: ${GRAY_ON_WHITE}; } ` @@ -71,7 +69,7 @@ const NotificationPrefsPane = ({ return (
- +
@@ -97,9 +95,7 @@ const NotificationPrefsPane = ({ - - {email} - + {email} ) : ( <> @@ -120,7 +116,7 @@ const NotificationPrefsPane = ({ ) })} - +
) } diff --git a/lib/components/user/phone-number-editor.tsx b/lib/components/user/phone-number-editor.tsx index 62f9c33e4..b53d142de 100644 --- a/lib/components/user/phone-number-editor.tsx +++ b/lib/components/user/phone-number-editor.tsx @@ -6,6 +6,7 @@ import React, { Component, createRef, Fragment } from 'react' import styled from 'styled-components' import { getAriaPhoneNumber } from '../../util/a11y' +import { GRAY_ON_WHITE } from '../util/colors' import { isBlank } from '../../util/ui' import InvisibleA11yLabel from '../util/invisible-a11y-label' @@ -18,7 +19,7 @@ import PhoneVerificationForm, { export type PhoneCodeRequestHandler = (phoneNumber: string) => void const PlainLink = styled.a` - color: #757575; + color: ${GRAY_ON_WHITE}; &:hover { text-decoration: none; } diff --git a/lib/components/user/styled.ts b/lib/components/user/styled.ts index 092dfdbd8..ae2c07e0d 100644 --- a/lib/components/user/styled.ts +++ b/lib/components/user/styled.ts @@ -77,7 +77,7 @@ export const TripPanelFooter = styled(Panel.Footer)` ` /** Formats non-
- + ) } diff --git a/lib/components/viewers/route-details.js b/lib/components/viewers/route-details.js index 7877f9681..48ba77c02 100644 --- a/lib/components/viewers/route-details.js +++ b/lib/components/viewers/route-details.js @@ -7,14 +7,13 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' import styled from 'styled-components' +import * as uiActions from '../../actions/ui' import { extractHeadsignFromPattern, getRouteColorBasedOnSettings } from '../../util/viewer' -import { findStopsForPattern } from '../../actions/api' import { getOperatorName } from '../../util/state' import { LinkOpensNewWindow } from '../util/externalLink' -import { setHoveredStop, setViewedRoute, setViewedStop } from '../../actions/ui' import { SortResultsDropdown } from '../util/dropdown' import { UnstyledButton } from '../util/unstyled-button' @@ -39,27 +38,14 @@ const PatternSelectButton = styled(UnstyledButton)` class RouteDetails extends Component { static propTypes = { - findStopsForPattern: findStopsForPattern.type, operator: PropTypes.shape({ defaultRouteColor: PropTypes.string, defaultRouteTextColor: PropTypes.string, longNameSplitter: PropTypes.string }), // There are more items in pattern and route, but none mandatory - pattern: PropTypes.shape({ id: PropTypes.string }), - route: PropTypes.shape({ id: PropTypes.string }), - setHoveredStop: setHoveredStop.type, - setViewedRoute: setViewedRoute.type - } - - /** - * Requests stop list for current pattern - */ - getStops = () => { - const { findStopsForPattern, pattern, route } = this.props - if (pattern && route) { - findStopsForPattern({ patternId: pattern.id, routeId: route.id }) - } + patternId: PropTypes.string, + route: PropTypes.shape({ id: PropTypes.string }) } /** @@ -80,8 +66,9 @@ class RouteDetails extends Component { } render() { - const { intl, operator, pattern, route, setHoveredStop } = this.props + const { intl, operator, patternId, route, setHoveredStop } = this.props const { agency, patterns = {}, shortName, url } = route + const pattern = patterns[patternId] const routeColor = getRouteColorBasedOnSettings(operator, route) @@ -130,8 +117,7 @@ class RouteDetails extends Component { const patternSelectLabel = intl.formatMessage({ id: 'components.RouteDetails.selectADirection' }) - const patternSelectName = - (pattern && patterns[pattern.id]?.headsign) || patternSelectLabel + const patternSelectName = pattern?.headsign || patternSelectLabel // if no pattern is set, we are in the routeRow return ( @@ -243,10 +229,9 @@ class RouteDetails extends Component { // connect to redux store const mapDispatchToProps = { - findStopsForPattern, - setHoveredStop, - setViewedRoute, - setViewedStop + setHoveredStop: uiActions.setHoveredStop, + setViewedRoute: uiActions.setViewedRoute, + setViewedStop: uiActions.setViewedStop } export default connect(null, mapDispatchToProps)(injectIntl(RouteDetails)) From b64304ac57a73cef88bc32909d95ea3ba15de810 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 25 Aug 2023 18:09:52 -0400 Subject: [PATCH 29/49] refactor(route-details): Convert to TypeScript, add missing types. --- lib/components/util/types.ts | 30 ++++---- .../{route-details.js => route-details.tsx} | 72 +++++++++++-------- .../viewers/{styled.js => styled.ts} | 16 +++-- 3 files changed, 69 insertions(+), 49 deletions(-) rename lib/components/viewers/{route-details.js => route-details.tsx} (83%) rename lib/components/viewers/{styled.js => styled.ts} (89%) diff --git a/lib/components/util/types.ts b/lib/components/util/types.ts index d75469556..febaa585d 100644 --- a/lib/components/util/types.ts +++ b/lib/components/util/types.ts @@ -1,3 +1,5 @@ +import { Route } from '@opentripplanner/types' + // TYPESCRIPT TODO: move this to a larger shared types file, preferably within otp-ui export interface StopData { bikeRental: BikeRental @@ -22,15 +24,6 @@ export interface BikeRental { stations: any[] } -export interface Route { - agencyId: string - agencyName: string - id: string - longName: string - mode: string - sortOrder: number -} - // FIXME: incomplete export interface StopTime { departureDelay: number @@ -45,6 +38,11 @@ export interface Pattern { desc: string headsign: string id: string + patternGeometry?: { + length: number + points: string + } + stops?: StopData[] } export interface Time { @@ -80,14 +78,18 @@ export interface ViewedRouteState { routeId: string } +export interface RouteVehicle { + patternId: string +} + // Routes have many properties beside id, but none of these are guaranteed. -export interface ViewedRouteObject { - id: string - longName?: string +export interface ViewedRouteObject extends Route { patterns?: Record pending?: boolean - shortName?: string - textColor?: string + url?: string + vehicles?: RouteVehicle[] } export type SetViewedRouteHandler = (route?: ViewedRouteState) => void + +export type SetViewedStopHandler = (payload: { stopId: string }) => void diff --git a/lib/components/viewers/route-details.js b/lib/components/viewers/route-details.tsx similarity index 83% rename from lib/components/viewers/route-details.js rename to lib/components/viewers/route-details.tsx index 48ba77c02..2dbc4e3fe 100644 --- a/lib/components/viewers/route-details.js +++ b/lib/components/viewers/route-details.tsx @@ -1,9 +1,7 @@ -// FIXME: typescript -/* eslint-disable react/prop-types */ import { connect } from 'react-redux' -import { FormattedMessage, injectIntl } from 'react-intl' +import { FormattedMessage, injectIntl, IntlShape } from 'react-intl' import { getMostReadableTextColor } from '@opentripplanner/core-utils/lib/route' -import PropTypes from 'prop-types' +import { TransitOperator } from '@opentripplanner/types' import React, { Component } from 'react' import styled from 'styled-components' @@ -14,6 +12,11 @@ import { } from '../../util/viewer' import { getOperatorName } from '../../util/state' import { LinkOpensNewWindow } from '../util/externalLink' +import { + SetViewedRouteHandler, + SetViewedStopHandler, + ViewedRouteObject +} from '../util/types' import { SortResultsDropdown } from '../util/dropdown' import { UnstyledButton } from '../util/unstyled-button' @@ -36,23 +39,28 @@ const PatternSelectButton = styled(UnstyledButton)` } ` -class RouteDetails extends Component { - static propTypes = { - operator: PropTypes.shape({ - defaultRouteColor: PropTypes.string, - defaultRouteTextColor: PropTypes.string, - longNameSplitter: PropTypes.string - }), - // There are more items in pattern and route, but none mandatory - patternId: PropTypes.string, - route: PropTypes.shape({ id: PropTypes.string }) - } +interface PatternSummary { + geometryLength: number + headsign: string + id: string +} + +interface Props { + intl: IntlShape + operator: TransitOperator + patternId: string + route: ViewedRouteObject + setHoveredStop: (id: string | null) => void + setViewedRoute: SetViewedRouteHandler + setViewedStop: SetViewedStopHandler +} +class RouteDetails extends Component { /** * If a headsign link is clicked, set that pattern in redux state so that the * view can adjust */ - _headSignButtonClicked = (id) => { + _headSignButtonClicked = (id: string) => { const { route, setViewedRoute } = this.props setViewedRoute({ patternId: id, routeId: route.id }) } @@ -60,7 +68,7 @@ class RouteDetails extends Component { /** * If a stop link is clicked, redirect to stop viewer */ - _stopLinkClicked = (stopId) => { + _stopLinkClicked = (stopId: string) => { const { setViewedStop } = this.props setViewedStop({ stopId }) } @@ -73,14 +81,16 @@ class RouteDetails extends Component { const routeColor = getRouteColorBasedOnSettings(operator, route) const headsigns = Object.entries(patterns) - .map(([id, pat]) => ({ - geometryLength: pat.geometry?.length, - headsign: extractHeadsignFromPattern(pat, shortName), - id - })) + .map( + ([id, pat]): PatternSummary => ({ + geometryLength: pat.patternGeometry?.length || 0, + headsign: extractHeadsignFromPattern(pat, shortName), + id + }) + ) // Remove duplicate headsigns. Using a reducer means that the first pattern // with a specific headsign is the accepted one. TODO: is this good behavior? - .reduce((prev, cur) => { + .reduce((prev: PatternSummary[], cur) => { const amended = prev const alreadyExistingIndex = prev.findIndex( (h) => h.headsign === cur.headsign @@ -100,12 +110,12 @@ class RouteDetails extends Component { }, []) .sort((a, b) => { // sort by number of vehicles on that pattern - const aVehicleCount = route.vehicles?.filter( - (vehicle) => vehicle.patternId === a.id - ).length - const bVehicleCount = route.vehicles?.filter( - (vehicle) => vehicle.patternId === b.id - ).length + const aVehicleCount = + route.vehicles?.filter((vehicle) => vehicle.patternId === a.id) + .length || 0 + const bVehicleCount = + route.vehicles?.filter((vehicle) => vehicle.patternId === b.id) + .length || 0 // if both have the same count, sort by pattern geometry length if (aVehicleCount === bVehicleCount) { @@ -164,7 +174,7 @@ class RouteDetails extends Component { pullRight style={{ color: 'black' }} > - {headsigns.map((h) => ( + {headsigns.map((h: PatternSummary) => (
  • this._headSignButtonClicked(h.id)} @@ -182,7 +192,7 @@ class RouteDetails extends Component {

    diff --git a/lib/components/viewers/styled.js b/lib/components/viewers/styled.ts similarity index 89% rename from lib/components/viewers/styled.js rename to lib/components/viewers/styled.ts index e1a4bd984..16bfbe173 100644 --- a/lib/components/viewers/styled.js +++ b/lib/components/viewers/styled.ts @@ -1,7 +1,14 @@ import styled from 'styled-components' +interface RenderProps { + backgroundColor?: string + full?: boolean + routeColor?: string + textColor?: string +} + /** Route Details */ -export const Container = styled.div` +export const Container = styled.div` background-color: ${(props) => props.full ? props.backgroundColor || '#ddd' : 'inherit'}; color: ${(props) => (props.full ? props.textColor : 'inherit')}; @@ -60,7 +67,7 @@ export const PatternContainer = styled.div` } ` -export const StopContainer = styled.ol` +export const StopContainer = styled.ol` color: ${(props) => props?.textColor || '#333'}; background-color: ${(props) => props?.backgroundColor || '#fff'}; overflow-y: scroll; @@ -69,7 +76,7 @@ export const StopContainer = styled.ol` are shown when browsers don't calculate 100% sensibly */ padding: 15px 0 100px; ` -export const StopLink = styled.button` +export const StopLink = styled.button` color: ${(props) => props?.textColor + 'da' || '#333'}; background-color: transparent; border: none; @@ -82,7 +89,8 @@ export const StopLink = styled.button` text-decoration: underline; } ` -export const Stop = styled.li` + +export const Stop = styled.li` cursor: pointer; display: block; white-space: nowrap; From dba0955402098006461e32b18082a386c9fa498a Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:16:22 -0400 Subject: [PATCH 30/49] refactor(route-details): Use logic to determine headsign with pattern. --- lib/components/viewers/route-details.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/components/viewers/route-details.tsx b/lib/components/viewers/route-details.tsx index 2dbc4e3fe..f5ee0d53c 100644 --- a/lib/components/viewers/route-details.tsx +++ b/lib/components/viewers/route-details.tsx @@ -127,7 +127,9 @@ class RouteDetails extends Component { const patternSelectLabel = intl.formatMessage({ id: 'components.RouteDetails.selectADirection' }) - const patternSelectName = pattern?.headsign || patternSelectLabel + + const patternSelectName = + extractHeadsignFromPattern(pattern, shortName) || patternSelectLabel // if no pattern is set, we are in the routeRow return ( From af56ed50d15a03d667a13000b2fd77e073cdcb2f Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:30:44 -0400 Subject: [PATCH 31/49] fix(route-details): Use index as key in stop list. --- lib/components/viewers/route-details.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/components/viewers/route-details.tsx b/lib/components/viewers/route-details.tsx index f5ee0d53c..4c0524b63 100644 --- a/lib/components/viewers/route-details.tsx +++ b/lib/components/viewers/route-details.tsx @@ -129,7 +129,8 @@ class RouteDetails extends Component { }) const patternSelectName = - extractHeadsignFromPattern(pattern, shortName) || patternSelectLabel + (pattern ? extractHeadsignFromPattern(pattern, shortName) : null) || + patternSelectLabel // if no pattern is set, we are in the routeRow return ( @@ -205,9 +206,10 @@ class RouteDetails extends Component { onMouseLeave={() => setHoveredStop(null)} textColor={getMostReadableTextColor(routeColor, route?.textColor)} > - {pattern?.stops?.map((stop) => ( + {pattern?.stops?.map((stop, index) => ( this._stopLinkClicked(stop.id)} onFocus={() => setHoveredStop(stop.id)} onMouseOver={() => setHoveredStop(stop.id)} From 85527a4296723f9b0de3bf3d80a23ba5b51cd718 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:29:12 -0400 Subject: [PATCH 32/49] refactor(route-details): Tweak a11y and headsign lookup. --- lib/components/viewers/route-details.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/components/viewers/route-details.tsx b/lib/components/viewers/route-details.tsx index 4c0524b63..579e376cc 100644 --- a/lib/components/viewers/route-details.tsx +++ b/lib/components/viewers/route-details.tsx @@ -129,10 +129,9 @@ class RouteDetails extends Component { }) const patternSelectName = - (pattern ? extractHeadsignFromPattern(pattern, shortName) : null) || + headsigns.find((h) => h.id === pattern?.id)?.headsign || patternSelectLabel - // if no pattern is set, we are in the routeRow return ( { // Use array index instead of stop id because a stop can be visited several times. key={index} onClick={() => this._stopLinkClicked(stop.id)} - onFocus={() => setHoveredStop(stop.id)} onMouseOver={() => setHoveredStop(stop.id)} routeColor={ routeColor.includes('ffffff') ? '#333' : routeColor @@ -224,6 +222,7 @@ class RouteDetails extends Component { this._stopLinkClicked(stop.id)} + onFocus={() => setHoveredStop(stop.id)} textColor={getMostReadableTextColor( routeColor, route?.textColor From 257cac2f40ce5533e1d11b34c5efe731b4f16372 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:17:12 -0400 Subject: [PATCH 33/49] fix(metro-itinerary): Display all routes in dashed lines on mouse leave. --- lib/components/narrative/metro/metro-itinerary.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/components/narrative/metro/metro-itinerary.tsx b/lib/components/narrative/metro/metro-itinerary.tsx index 26ffa98a6..4e65c966d 100644 --- a/lib/components/narrative/metro/metro-itinerary.tsx +++ b/lib/components/narrative/metro/metro-itinerary.tsx @@ -202,23 +202,17 @@ class MetroItinerary extends NarrativeItinerary { static ModesAndRoutes = MetroItineraryRoutes _onMouseEnter = () => { - const { active, index, setVisibleItinerary, visibleItinerary } = this.props + const { active, index, setVisibleItinerary, visible } = this.props // Set this itinerary as visible if not already visible. - const visibleNotSet = - visibleItinerary === null || visibleItinerary === undefined - const isVisible = - visibleItinerary === index || (active === index && visibleNotSet) + const isVisible = visible || active === index if (typeof setVisibleItinerary === 'function' && !isVisible) { setVisibleItinerary({ index }) } } _onMouseLeave = () => { - const { index, setVisibleItinerary, visibleItinerary } = this.props - if ( - typeof setVisibleItinerary === 'function' && - visibleItinerary === index - ) { + const { setVisibleItinerary, visible } = this.props + if (typeof setVisibleItinerary === 'function' && visible) { setVisibleItinerary({ index: null }) } } From c23143a4deca538286356e02284e882f20d8c682 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Mon, 28 Aug 2023 14:53:40 -0700 Subject: [PATCH 34/49] deps: update new walkspeed deps --- lib/reducers/create-otp-reducer.js | 2 +- package.json | 8 ++--- yarn.lock | 56 +++++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index bc866f3ea..14c002f36 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -131,7 +131,7 @@ export function getInitialState(userDefinedConfig) { config.api.path = `/otp/routers/${routerId}` } - let initialModeSettings = defaultModeSettings + let initialModeSettings = defaultModeSettings || [] if (!initialModeSettings.length) { initialModeSettings = [] } diff --git a/package.json b/package.json index 83d45ab65..c8ea4eaea 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@bugsnag/plugin-react": "^7.17.0", "@floating-ui/react": "^0.19.2", "@opentripplanner/base-map": "^3.0.13", - "@opentripplanner/core-utils": "^9.0.3", + "@opentripplanner/core-utils": "^11.0.0", "@opentripplanner/endpoints-overlay": "^2.0.7", "@opentripplanner/from-to-location-picker": "^2.1.7", "@opentripplanner/geocoder": "^1.4.2", @@ -58,7 +58,7 @@ "@opentripplanner/transit-vehicle-overlay": "^4.0.4", "@opentripplanner/transitive-overlay": "^3.0.13", "@opentripplanner/trip-details": "^5.0.2", - "@opentripplanner/trip-form": "^3.1.1", + "@opentripplanner/trip-form": "^3.3.1", "@opentripplanner/trip-viewer-overlay": "^2.0.5", "@opentripplanner/vehicle-rental-overlay": "^2.1.1", "@styled-icons/fa-regular": "^10.34.0", @@ -132,7 +132,7 @@ "@craco/craco": "^6.3.0", "@jackwilsdon/craco-use-babelrc": "^1.0.0", "@opentripplanner/scripts": "^1.2.0", - "@opentripplanner/types": "^6.0.0", + "@opentripplanner/types": "^6.1.0", "@percy/cli": "^1.20.3", "@percy/puppeteer": "^2.0.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", @@ -240,4 +240,4 @@ "percy-css": ".percy-hide { opacity: 0!important; } " } } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0835fc215..724a0c63f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2394,6 +2394,44 @@ maplibre-gl "^2.1.9" react-map-gl "^7.0.15" +"@opentripplanner/core-utils@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-10.0.0.tgz#ab5cba39097e2b15e634b1e70141076f3a5614d3" + integrity sha512-3T+P9GlBmeL8AHATUXbOouDtr3eYNv6VYMbVagFV9MBhYf3wSCJ3kAdBEWK+TQGfiWRfcxVJueuy8kvmVrkJtA== + dependencies: + "@conveyal/lonlat" "^1.4.1" + "@mapbox/polyline" "^1.1.0" + "@opentripplanner/geocoder" "^1.4.1" + "@styled-icons/foundation" "^10.34.0" + "@turf/along" "^6.0.1" + bowser "^2.7.0" + chroma-js "^2.4.2" + date-fns "^2.28.0" + date-fns-tz "^1.2.2" + graphql "^16.6.0" + lodash.clonedeep "^4.5.0" + lodash.isequal "^4.5.0" + qs "^6.9.1" + +"@opentripplanner/core-utils@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.0.0.tgz#dd40b7cd68d7d7bf4f56d87bf708d6bd4b7660ed" + integrity sha512-b+oPQWKpzXHeq9QdU0JmsplZX2QIRj4ARdIPJ++f5i/hpIxMnbKApwDHVkRdHz6kYCOQI7pcVNTunT7AGNvudg== + dependencies: + "@conveyal/lonlat" "^1.4.1" + "@mapbox/polyline" "^1.1.0" + "@opentripplanner/geocoder" "^1.4.1" + "@styled-icons/foundation" "^10.34.0" + "@turf/along" "^6.0.1" + bowser "^2.7.0" + chroma-js "^2.4.2" + date-fns "^2.28.0" + date-fns-tz "^1.2.2" + graphql "^16.6.0" + lodash.clonedeep "^4.5.0" + lodash.isequal "^4.5.0" + qs "^6.9.1" + "@opentripplanner/core-utils@^8.1.1", "@opentripplanner/core-utils@^8.2.1": version "8.3.2" resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-8.3.2.tgz#5095dd3ae8493e478b3f599353c6804b35f1e1dd" @@ -2694,13 +2732,13 @@ flat "^5.0.2" react-animate-height "^3.0.4" -"@opentripplanner/trip-form@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-3.1.1.tgz#67d01f1fcfd31ccc758907b4324e453f2984f8b2" - integrity sha512-A5OFHZyLW4CZDndrdjLy3ZHSQGWgaido/yN/jZXKcBHRDZK91Izd0xNAn4p4EvwpULPocg1mak/2dYwl07TWUQ== +"@opentripplanner/trip-form@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-3.3.1.tgz#3cf3212d1948ff28440600a6951847bf7a77467a" + integrity sha512-XDioTMNXSugz1iPWRVeP91xqJYbEOI/ZdEL9fHXllIT5c8dkg35mjCNtGtOJzaZZVtY+p3KxzD4lEWLVdAD45w== dependencies: "@floating-ui/react" "^0.19.2" - "@opentripplanner/core-utils" "^8.2.1" + "@opentripplanner/core-utils" "^10.0.0" "@styled-icons/bootstrap" "^10.34.0" "@styled-icons/boxicons-regular" "^10.38.0" "@styled-icons/fa-regular" "^10.37.0" @@ -2719,10 +2757,10 @@ "@opentripplanner/base-map" "^3.0.11" "@opentripplanner/core-utils" "^8.2.1" -"@opentripplanner/types@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.0.0.tgz#3dabebd404c88030f4191f08c7692d72ca2e3c8d" - integrity sha512-mwwj2WzkdCoG2KUYaR/4I2JkcuXr1fFUJpP4nk10YbRgzyGtGLaGIdiTXNYpwgk/5//pMHQ5Qa6psWrHpqRm1A== +"@opentripplanner/types@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.1.0.tgz#dd5b88cc0b73939cd1eb5bd44d4768e21bedaacc" + integrity sha512-fFuNMJLrSCIoIWJ7VugM1Jb7HfIcRDzb8o2LNsASExWAEYDuONFxyGYT/98g82/70Grl8kCSMSAFi0lEiQ/cPQ== "@opentripplanner/vehicle-rental-overlay@^2.1.1": version "2.1.1" From 91055c878240cf7c722e8cd97e72bc4fcdfdace6 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:51:28 -0400 Subject: [PATCH 35/49] refactor(metro-itinerary): Update interpretation of active. --- lib/components/narrative/metro/metro-itinerary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/narrative/metro/metro-itinerary.tsx b/lib/components/narrative/metro/metro-itinerary.tsx index 4e65c966d..cbd0f7194 100644 --- a/lib/components/narrative/metro/metro-itinerary.tsx +++ b/lib/components/narrative/metro/metro-itinerary.tsx @@ -204,7 +204,7 @@ class MetroItinerary extends NarrativeItinerary { _onMouseEnter = () => { const { active, index, setVisibleItinerary, visible } = this.props // Set this itinerary as visible if not already visible. - const isVisible = visible || active === index + const isVisible = visible || active if (typeof setVisibleItinerary === 'function' && !isVisible) { setVisibleItinerary({ index }) } From 2dacb7b4b383882083032b056b70dae9c03a517e Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:11:57 -0400 Subject: [PATCH 36/49] fix(actions/location): Display alert if location was denied. Also, don't fetch location on loading the desktop view. --- i18n/en-US.yml | 5 +++++ lib/actions/location.tsx | 10 ++++++++++ lib/components/app/responsive-webapp.js | 6 ++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index 613156575..e518a7672 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -34,6 +34,11 @@ actions: setPaymentError: "Error setting payment info:" setRequestStatusError: "Error setting request status:" location: + deniedAccessAlert: > + Access to your location is blocked. + + To use your current location, enable location permissions from your + browser, and reload the page. geolocationNotSupportedError: Geolocation not supported by your browser unknownPositionError: Unknown error getting position map: diff --git a/lib/actions/location.tsx b/lib/actions/location.tsx index a336efb18..64e9e8921 100644 --- a/lib/actions/location.tsx +++ b/lib/actions/location.tsx @@ -2,6 +2,7 @@ import { createAction } from 'redux-actions' import { Dispatch } from 'redux' import { IntlShape } from 'react-intl' +import { isMobile } from '@opentripplanner/core-utils/lib/ui' import { setLocationToCurrent } from './map' @@ -47,6 +48,15 @@ export function getCurrentPosition( // On error (error) => { console.log('error getting current position', error) + // On desktop, after user clicks "Use location" from the location fields, + // show an alert and explain if location is blocked. + if (!isMobile() && error.code === 1) { + window.alert( + intl.formatMessage({ + id: 'actions.location.deniedAccessAlert' + }) + ) + } // FIXME, analyze error code to produce better error message. // See https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError dispatch(receivedPositionError({ error })) diff --git a/lib/components/app/responsive-webapp.js b/lib/components/app/responsive-webapp.js index e135c16b1..0ae74b8a7 100644 --- a/lib/components/app/responsive-webapp.js +++ b/lib/components/app/responsive-webapp.js @@ -153,11 +153,9 @@ class ResponsiveWebapp extends Component { } } - // Test location availability on load, - // so it is reported correctly by the location fields. - getCurrentPosition(intl) - if (isMobile()) { + // Test location availability on load + getCurrentPosition(intl) // Also, watch for changes in position on mobile navigator.geolocation.watchPosition( // On success From 11e8a613640ee0b0a343fe2617832b4e466e9c44 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:54:36 -0400 Subject: [PATCH 37/49] improvement(actions/location): Add i18n to user-denied error messages. --- i18n/en-US.yml | 1 + lib/actions/location.tsx | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/i18n/en-US.yml b/i18n/en-US.yml index e518a7672..8c438529b 100644 --- a/i18n/en-US.yml +++ b/i18n/en-US.yml @@ -41,6 +41,7 @@ actions: browser, and reload the page. geolocationNotSupportedError: Geolocation not supported by your browser unknownPositionError: Unknown error getting position + userDeniedPermission: User denied permission map: currentLocation: (Current Location) user: diff --git a/lib/actions/location.tsx b/lib/actions/location.tsx index 64e9e8921..40ffeaa93 100644 --- a/lib/actions/location.tsx +++ b/lib/actions/location.tsx @@ -57,9 +57,19 @@ export function getCurrentPosition( }) ) } - // FIXME, analyze error code to produce better error message. - // See https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError - dispatch(receivedPositionError({ error })) + const newError = { ...error } + if (error.code === 1) { + // i18n for user-denied location message (error.code = 1 on secure origins). + if ( + window.location.protocol === 'https:' || + window.location.host.startsWith('localhost:') + ) { + newError.message = intl.formatMessage({ + id: 'actions.location.userDeniedPermission' + }) + } + } + dispatch(receivedPositionError({ error: newError })) }, // Options { enableHighAccuracy: true } From 1b84270e8789820a5ded4a077a568b7304dc690e Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:03:38 -0400 Subject: [PATCH 38/49] chore(i18n): Add missing French. --- i18n/fr.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/i18n/fr.yml b/i18n/fr.yml index 742db4275..5c41e72fc 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -38,8 +38,14 @@ actions: setPaymentError: "Erreur sur les coordonnées de paiement :" setRequestStatusError: "Erreur sur l'état de la requête :" location: + deniedAccessAlert: > + L'accès à votre position est refusé. + + Pour utiliser votre emplacement actuel, permettez-en l'accès depuis votre + navigateur, et ouvrez de nouveau cette page. geolocationNotSupportedError: La géolocalisation n'est pas prise en charge par votre navigateur. unknownPositionError: Erreur inconnue lors de la détection de votre emplacement. + userDeniedPermission: Refusé par l'utilisateur map: currentLocation: (Emplacement actuel) user: @@ -178,7 +184,7 @@ components: AdvancedOptions: bannedRoutes: Choisissez les lignes à éviter… bikeTolerance: Tolérance au vélo - preferredRoutes: Choisissez les lignes preferées + preferredRoutes: Choisissez les lignes préferées walkTolerance: Tolérance à la marche AfterSignInScreen: mainTitle: Redirection... From 933d34a0c8fc1838653a27433651b5e21b92c218 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:37:38 -0400 Subject: [PATCH 39/49] refactor(actions/location): Apply PR review feedback. --- lib/actions/location.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/actions/location.tsx b/lib/actions/location.tsx index 40ffeaa93..c62e9b740 100644 --- a/lib/actions/location.tsx +++ b/lib/actions/location.tsx @@ -50,6 +50,7 @@ export function getCurrentPosition( console.log('error getting current position', error) // On desktop, after user clicks "Use location" from the location fields, // show an alert and explain if location is blocked. + // TODO: Consider moving the handling of unavailable location to the location-field component. if (!isMobile() && error.code === 1) { window.alert( intl.formatMessage({ From 381f9edbc4184644da2b5b91fa4725d12d8d7fcf Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 30 Aug 2023 12:36:02 -0400 Subject: [PATCH 40/49] refactor: address pr feedback --- lib/components/user/sequential-pane-display.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/components/user/sequential-pane-display.tsx b/lib/components/user/sequential-pane-display.tsx index adf171e21..e41a953b7 100644 --- a/lib/components/user/sequential-pane-display.tsx +++ b/lib/components/user/sequential-pane-display.tsx @@ -52,7 +52,7 @@ class SequentialPaneDisplay extends Component> { h1Ref = React.createRef() - _handleFocus = () => { + _focusHeader = () => { this.h1Ref?.current?.focus() } @@ -76,7 +76,7 @@ class SequentialPaneDisplay extends Component> { this._routeTo(nextId) } } - this._handleFocus() + this._focusHeader() } _handleToPrevPane = () => { @@ -85,11 +85,11 @@ class SequentialPaneDisplay extends Component> { const prevId = panes[activePaneIndex - 1].id prevId && this._routeTo(prevId) } - this._handleFocus() + this._focusHeader() } componentDidMount(): void { - this._handleFocus() + this._focusHeader() } render() { From 1870e93eb30445fae0ba97d994b73701437763d8 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 22 Aug 2023 14:56:33 -0400 Subject: [PATCH 41/49] fix(create-otp-reducer): Ensure that responses for mode combos are stored in same order as the combo --- lib/actions/apiV2.js | 3 ++- lib/components/narrative/narrative-itineraries.js | 2 +- lib/reducers/create-otp-reducer.js | 9 ++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index 3b9eba694..839eb7fea 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -919,7 +919,7 @@ export function routingQuery(searchId = null, updateSearchInReducer) { dispatch(setItineraryView(ItineraryView.LIST)) - combinations.forEach((combo) => { + combinations.forEach((combo, index) => { const query = generateOtp2Query(combo) dispatch( createGraphQLQueryAction( @@ -982,6 +982,7 @@ export function routingQuery(searchId = null, updateSearchInReducer) { } return { + index, response: { plan: { ...response.data?.plan, diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js index 23d1d96fe..4fa470537 100644 --- a/lib/components/narrative/narrative-itineraries.js +++ b/lib/components/narrative/narrative-itineraries.js @@ -560,7 +560,7 @@ class NarrativeItineraries extends Component { } const reduceErrorsFromResponse = (acc, cur) => { - const { routingErrors } = cur?.plan + const { routingErrors } = cur?.plan || {} if (routingErrors) { routingErrors.forEach((routingError) => { const { code, inputField } = routingError diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 172ccb710..b5534a071 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -325,7 +325,14 @@ function createOtpReducer(config) { searches: { [searchId]: { pending: { $set: activeSearch?.pending - 1 }, - response: { $push: [response] } + response: + action.payload.index !== undefined + ? { + [action.payload.index]: { + $set: response + } + } + : { $push: [response] } } }, ui: { From 6954113e49e8541bdf35af84cd6c24b4a0d48f68 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 31 Aug 2023 12:03:14 -0400 Subject: [PATCH 42/49] refactor(notification-prefs-pane): Add config setting for showing push notification settings. --- example-config.yml | 1 + .../user/notification-prefs-pane.tsx | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/example-config.yml b/example-config.yml index b725f59a4..63fbf78cc 100644 --- a/example-config.yml +++ b/example-config.yml @@ -68,6 +68,7 @@ persistence: # otp_middleware: # apiBaseUrl: https://otp-middleware.example.com # apiKey: your-middleware-api-key +# supportsPushNotifications: true # If not set, push notification settings will not be shown. ### Adding additional menu items to the main menu items. Use the separator flag ### to include a separator line if you have groups of menu items diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 3636dedec..85668867c 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -1,6 +1,7 @@ +import { connect } from 'react-redux' import { Field, FormikProps } from 'formik' import { FormattedMessage } from 'react-intl' -import React, { Fragment } from 'react' +import React from 'react' import styled from 'styled-components' import { GRAY_ON_WHITE } from '../util/colors' @@ -13,6 +14,7 @@ import PhoneNumberEditor, { } from './phone-number-editor' interface Props extends FormikProps { + allowedNotificationChannels: string[] loggedInUser: User onRequestPhoneVerificationCode: PhoneCodeRequestHandler onSendPhoneVerificationCode: PhoneVerificationSubmitHandler @@ -21,7 +23,8 @@ interface Props extends FormikProps { } } -const allowedNotificationChannels = ['email', 'sms', 'push'] +const allNotificationChannels = ['email', 'sms', 'push'] +const emailAndSms = ['email', 'sms'] // Styles const NotificationOption = styled.div` @@ -52,6 +55,7 @@ const NotificationOption = styled.div` * User notification preferences pane. */ const NotificationPrefsPane = ({ + allowedNotificationChannels, onRequestPhoneVerificationCode, onSendPhoneVerificationCode, phoneFormatOptions, @@ -115,4 +119,14 @@ const NotificationPrefsPane = ({ ) } -export default NotificationPrefsPane +const mapStateToProps = (state: any) => { + const { supportsPushNotifications } = + state.otp.config.persistence?.otp_middleware || {} + return { + allowedNotificationChannels: supportsPushNotifications + ? allNotificationChannels + : emailAndSms + } +} + +export default connect(mapStateToProps)(NotificationPrefsPane) From 97dca1d73ef0f31656d83ba1b2914aa0b424dafb Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 31 Aug 2023 12:17:01 -0400 Subject: [PATCH 43/49] refactor(notification-prefs-pane): Reduce phone options prop drilling. --- lib/components/user/notification-prefs-pane.tsx | 3 ++- lib/components/user/user-account-screen.js | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 85668867c..ffe092eb3 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -125,7 +125,8 @@ const mapStateToProps = (state: any) => { return { allowedNotificationChannels: supportsPushNotifications ? allNotificationChannels - : emailAndSms + : emailAndSms, + phoneFormatOptions: state.otp.config.phoneFormatOptions } } diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index 6b05245be..78e756d01 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -110,8 +110,7 @@ class UserAccountScreen extends Component { } render() { - const { auth0, isCreating, itemId, loggedInUser, phoneFormatOptions } = - this.props + const { auth0, isCreating, itemId, loggedInUser } = this.props const DisplayComponent = isCreating ? NewAccountWizard : ExistingAccountDisplay @@ -153,7 +152,6 @@ class UserAccountScreen extends Component { onSendPhoneVerificationCode={ this._handleSendPhoneVerificationCode } - phoneFormatOptions={phoneFormatOptions} /> ) @@ -173,8 +171,7 @@ const mapStateToProps = (state, ownProps) => { return { isCreating, itemId: step, - loggedInUser: state.user.loggedInUser, - phoneFormatOptions: state.otp.config.phoneFormatOptions + loggedInUser: state.user.loggedInUser } } From 344442f42157939bef5dd4fc69bfcbdb6fe36ff2 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 31 Aug 2023 12:41:04 -0400 Subject: [PATCH 44/49] improvement(notification-prefs-pane): Improve checkbox layout --- .../user/notification-prefs-pane.tsx | 98 ++++++++++--------- lib/components/user/phone-number-editor.tsx | 2 +- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index ffe092eb3..127fcb10c 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -1,6 +1,7 @@ import { connect } from 'react-redux' import { Field, FormikProps } from 'formik' import { FormattedMessage } from 'react-intl' +import { ListGroup, ListGroupItem } from 'react-bootstrap' import React from 'react' import styled from 'styled-components' @@ -27,10 +28,9 @@ const allNotificationChannels = ['email', 'sms', 'push'] const emailAndSms = ['email', 'sms'] // Styles -const NotificationOption = styled.div` +const NotificationOption = styled(ListGroupItem)` align-items: flex-start; display: flex; - margin-bottom: 10px; /* Match bootstrap's spacing between checkbox and label */ & > span:first-child { @@ -68,53 +68,55 @@ const NotificationPrefsPane = ({ - {allowedNotificationChannels.map((type) => { - // TODO: If removing the Save/Cancel buttons on the account screen, - // persist changes immediately when onChange is triggered. - const inputId = `notification-channel-${type}` - const inputDescriptionId = `${inputId}-description` - return ( - - - - - - - {type === 'email' ? ( - {email} - ) : type === 'sms' ? ( - + {allowedNotificationChannels.map((type) => { + // TODO: If removing the Save/Cancel buttons on the account screen, + // persist changes immediately when onChange is triggered. + const inputId = `notification-channel-${type}` + const inputDescriptionId = `${inputId}-description` + return ( + + + - ) : ( - - {pushDevices ? ( - // TODO: i18n - `${pushDevices} devices registered` - ) : ( - - )} - - )} - - - ) - })} + + + + {type === 'email' ? ( + {email} + ) : type === 'sms' ? ( + + ) : ( + + {pushDevices ? ( + // TODO: i18n + `${pushDevices} devices registered` + ) : ( + + )} + + )} + + + ) + })} + ) } diff --git a/lib/components/user/phone-number-editor.tsx b/lib/components/user/phone-number-editor.tsx index b53d142de..b3dc52efc 100644 --- a/lib/components/user/phone-number-editor.tsx +++ b/lib/components/user/phone-number-editor.tsx @@ -265,7 +265,7 @@ class PhoneNumberEditor extends Component { )} - + {ariaAlertContent} From e7b2aeebd7a27edab7c6a4595ebc15a8d5f88bab Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Fri, 1 Sep 2023 01:01:16 +0200 Subject: [PATCH 45/49] improvement(apiv2): use the OTP-UI conversion utility for legacy format --- lib/actions/apiV2.js | 49 ++++++-------------------------------------- 1 file changed, 6 insertions(+), 43 deletions(-) diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index 3b9eba694..ec8aec770 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -44,7 +44,11 @@ import { rememberPlace } from './user' import { setItineraryView } from './ui' import { zoomToPlace } from './map' -const { generateCombinations, generateOtp2Query } = coreUtils.queryGen +const { + convertGraphQLResponseToLegacy, + generateCombinations, + generateOtp2Query +} = coreUtils.queryGen const { getTripOptionsFromQuery, getUrlParams } = coreUtils.query const { randId } = coreUtils.storage @@ -782,47 +786,6 @@ const pickupDropoffTypeToOtp1 = (otp2Type) => { } } -/** - * Converts a leg from GraphQL format to legacy REST format. - * @param leg OTP2 GraphQL style leg - * @returns REST shaped leg - */ -const processLeg = (leg) => ({ - ...leg, - agencyBrandingUrl: leg.agency?.url, - agencyName: leg.agency?.name, - agencyUrl: leg.agency?.url, - alerts: aggregateAlerts( - leg.agency?.alerts, - leg.route?.alerts, - leg.to?.stop?.alerts, - leg.from?.stop?.alerts - ), - alightRule: pickupDropoffTypeToOtp1(leg.dropoffType), - boardRule: pickupDropoffTypeToOtp1(leg.pickupType), - dropOffBookingInfo: { - latestBookingTime: leg.dropOffBookingInfo - }, - from: { - ...leg.from, - stopCode: leg.from.stop?.code, - stopId: leg.from.stop?.gtfsId - }, - route: leg.route?.shortName, - routeColor: leg.route?.color, - routeId: leg.route?.id, - routeLongName: leg.route?.longName, - routeShortName: leg.route?.shortName, - routeTextColor: leg.route?.textColor, - to: { - ...leg.to, - stopCode: leg.to.stop?.code, - stopId: leg.to.stop?.gtfsId - }, - tripHeadsign: leg.trip?.tripHeadsign, - tripId: leg.trip?.gtfsId -}) - const queryParamConfig = { modeButtons: DelimitedArrayParam } export function routingQuery(searchId = null, updateSearchInReducer) { @@ -962,7 +925,7 @@ export function routingQuery(searchId = null, updateSearchInReducer) { const withCollapsedShortNames = response.data?.plan?.itineraries?.map((itin) => ({ ...itin, - legs: itin.legs?.map(processLeg) + legs: itin.legs?.map(convertGraphQLResponseToLegacy) })) /* It is possible for a NO_TRANSIT_CONNECTION error to be From bc341dea9436c2d3353de4bf023242542ce7d205 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Fri, 1 Sep 2023 12:46:26 +0200 Subject: [PATCH 46/49] fix incorrect import --- lib/actions/apiV2.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index ec8aec770..70f116d2a 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -44,12 +44,9 @@ import { rememberPlace } from './user' import { setItineraryView } from './ui' import { zoomToPlace } from './map' -const { - convertGraphQLResponseToLegacy, - generateCombinations, - generateOtp2Query -} = coreUtils.queryGen +const { generateCombinations, generateOtp2Query } = coreUtils.queryGen const { getTripOptionsFromQuery, getUrlParams } = coreUtils.query +const { convertGraphQLResponseToLegacy } = coreUtils.itinerary const { randId } = coreUtils.storage const LIGHT_GRAY = '666666' From 92ac6c941b335b824d90257f6222cffcfa2b09d2 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Sat, 2 Sep 2023 00:52:22 +0200 Subject: [PATCH 47/49] deps: update core-utils --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 770861132..5982b1c6f 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@bugsnag/plugin-react": "^7.17.0", "@floating-ui/react": "^0.19.2", "@opentripplanner/base-map": "^3.0.14", - "@opentripplanner/core-utils": "^11.0.0", + "@opentripplanner/core-utils": "^11.0.2", "@opentripplanner/endpoints-overlay": "^2.0.8", "@opentripplanner/from-to-location-picker": "^2.1.8", "@opentripplanner/geocoder": "^1.4.2", diff --git a/yarn.lock b/yarn.lock index 2d9cec407..d24a5c104 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2439,10 +2439,10 @@ lodash.isequal "^4.5.0" qs "^6.9.1" -"@opentripplanner/core-utils@^11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.0.0.tgz#dd40b7cd68d7d7bf4f56d87bf708d6bd4b7660ed" - integrity sha512-b+oPQWKpzXHeq9QdU0JmsplZX2QIRj4ARdIPJ++f5i/hpIxMnbKApwDHVkRdHz6kYCOQI7pcVNTunT7AGNvudg== +"@opentripplanner/core-utils@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.0.2.tgz#ebfa939c3b7b266e3bf47dcaddda88e093430a9d" + integrity sha512-3oQYvnhFHY88K61j2tUkU2fm8tDci5qaMVGEgqTzxpE5NmBTUdAWU1V7W9snJoRATzz3zy+K9qrtIhGjwbwlnA== dependencies: "@conveyal/lonlat" "^1.4.1" "@mapbox/polyline" "^1.1.0" From 31c000b6b2ff458f9985ef882f41520e45752c99 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:52:47 -0400 Subject: [PATCH 48/49] fix(otp reducer): Handle new FT search and updating FT responses. --- lib/reducers/create-otp-reducer.js | 37 +++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index b5534a071..91ee98d72 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -353,30 +353,55 @@ function createOtpReducer(config) { } }) case 'SET_PENDING_REQUESTS': + if (state.searches[searchId]) { + return update(state, { + searches: { + [searchId]: { + pending: { $set: action.payload?.pending } + } + } + }) + } return update(state, { searches: { [searchId]: { - pending: { $set: action.payload?.pending } + $set: { + pending: action.payload?.pending + } } } }) case 'SET_ACTIVE_ITINERARIES': const responseUpdate = {} + const responseSet = [] Object.entries(action.payload.assignedItinerariesByResponse).forEach( ([responseIdx, responsePlanItineraries]) => { - responseUpdate[responseIdx] = { + const responsePart = { plan: { - itineraries: { - $set: responsePlanItineraries - } + itineraries: responsePlanItineraries } } + responseUpdate[responseIdx] = { + $set: responsePart + } + responseSet[responseIdx] = responsePart } ) + if (state.searches[searchId]?.response) { + return update(state, { + searches: { + [searchId]: { + response: responseUpdate + } + } + }) + } return update(state, { searches: { [searchId]: { - response: responseUpdate + $set: { + response: responseSet + } } } }) From a7e82dc780c38bf302439457ee3f716b0edd6db9 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:22:00 -0400 Subject: [PATCH 49/49] fix(otp reducer): Update successive responses, fix ft wait for current search. --- lib/actions/field-trip.js | 23 ++++++---- lib/reducers/create-otp-reducer.js | 68 +++++++++++++++--------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/lib/actions/field-trip.js b/lib/actions/field-trip.js index ee77d2ca1..da34ec27b 100644 --- a/lib/actions/field-trip.js +++ b/lib/actions/field-trip.js @@ -687,7 +687,7 @@ function checkValidityAndCapacity(state, request) { // iterate through itineraries to check validity and assign field trip // groups - response.plan.itineraries.forEach((itinerary) => { + response.plan.itineraries.forEach((itinerary, itinIdx) => { let itineraryCapacity = Number.POSITIVE_INFINITY // check each individual trip to see if there aren't any trips in this @@ -744,12 +744,13 @@ function checkValidityAndCapacity(state, request) { // A field trip response is guaranteed to have only one itinerary, so it // ok to set the itinerary by response as an array with a single // itinerary. - assignedItinerariesByResponse[responseIdx] = [ - { - ...itinerary, - fieldTripGroupSize: Math.min(itineraryCapacity, remainingGroupSize) - } - ] + if (!assignedItinerariesByResponse[responseIdx]) { + assignedItinerariesByResponse[responseIdx] = {} + } + assignedItinerariesByResponse[responseIdx][itinIdx] = { + ...itinerary, + fieldTripGroupSize: Math.min(itineraryCapacity, remainingGroupSize) + } remainingGroupSize -= itineraryCapacity } }) @@ -844,8 +845,12 @@ function makeFieldTripPlanRequests(request, outbound, intl) { // I do not believe that it is worth the effort. I am instead in favor of // re-building field trip from the ground up as a separate application. setInterval(() => { - const activeItineraries = getActiveItineraries(getState()) - if (activeItineraries.length >= numRequests) { + const searchResponse = getState().otp.searches[searchId]?.response + const activeItineraries = + searchResponse?.reduce((prev, resp) => { + return (prev += resp?.plan?.itineraries?.length || 0) + }, 0) || 0 + if (activeItineraries >= numRequests) { resolve() } }, 20) diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 91ee98d72..0888474e9 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -327,12 +327,31 @@ function createOtpReducer(config) { pending: { $set: activeSearch?.pending - 1 }, response: action.payload.index !== undefined - ? { - [action.payload.index]: { - $set: response + ? state.searches[searchId].response?.[action.payload.index] + ? { + // Field Trip may make multiple requests, one itinerary at a time, + // to divide up large groups. In that case, append the itineraries to the plan array + // at the indicated response index, as if multiple itineraries were returned in one request. + [action.payload.index]: { + plan: { + itineraries: { + $push: [...response.plan.itineraries] + } + } + } } + : { + // Accommodate regular mode combination responses in the order they were requested + // to ensure that the response order remains the same if doing the same search again. + [action.payload.index]: { + $set: response + } + } + : { + // If queries were made outside of mode combinations, just add the responses + // in the order they are received (order may change if doing the same search again). + $push: [response] } - : { $push: [response] } } }, ui: { @@ -353,55 +372,34 @@ function createOtpReducer(config) { } }) case 'SET_PENDING_REQUESTS': - if (state.searches[searchId]) { - return update(state, { - searches: { - [searchId]: { - pending: { $set: action.payload?.pending } - } - } - }) - } return update(state, { searches: { [searchId]: { - $set: { - pending: action.payload?.pending - } + pending: { $set: action.payload?.pending } } } }) case 'SET_ACTIVE_ITINERARIES': const responseUpdate = {} - const responseSet = [] Object.entries(action.payload.assignedItinerariesByResponse).forEach( ([responseIdx, responsePlanItineraries]) => { - const responsePart = { + responseUpdate[responseIdx] = { plan: { - itineraries: responsePlanItineraries + itineraries: Object.entries(responsePlanItineraries).reduce( + (prev, [itinIdx, itin]) => { + prev[itinIdx] = { $set: itin } + return prev + }, + {} + ) } } - responseUpdate[responseIdx] = { - $set: responsePart - } - responseSet[responseIdx] = responsePart } ) - if (state.searches[searchId]?.response) { - return update(state, { - searches: { - [searchId]: { - response: responseUpdate - } - } - }) - } return update(state, { searches: { [searchId]: { - $set: { - response: responseSet - } + response: responseUpdate } } })