Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(alert): adapt alert handling to upstream #836

Merged
merged 5 commits into from
Dec 4, 2024
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
33 changes: 11 additions & 22 deletions app/component/AlertBanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'found';
import TruncateMarkup from 'react-truncate-markup';
import connectToStores from 'fluxible-addons-react/connectToStores';
import { alertShape, configShape } from '../util/shapes';
import Icon from './Icon';
import {
getServiceAlertDescription,
alertSeverityCompare,
getServiceAlertHeader,
} from '../util/alertUtils';
import { alertSeverityCompare } from '../util/alertUtils';

const AlertBanner = ({ alerts, linkAddress, language }, { config }) => {
const AlertBanner = ({ alerts, linkAddress }, { config }) => {
const alert = [...alerts].sort(alertSeverityCompare)[0];
const message = getServiceAlertDescription(alert, language);
const header = getServiceAlertHeader(alert, language);
const message = alert.alertDescriptionText;
const header = alert.alertHeaderText;
if (!message && !header) {
return <></>;
return null;
}
const icon =
alert.alertSeverityLevel !== 'INFO'
Expand All @@ -41,26 +37,19 @@ const AlertBanner = ({ alerts, linkAddress, language }, { config }) => {
<Icon
img="icon-icon_arrow-collapse--right"
color={config.colors.primary}
/>{' '}
/>
</div>
</div>
</Link>
);
};

const connectedComponent = connectToStores(
AlertBanner,
['PreferencesStore'],
({ getStore }) => ({
language: getStore('PreferencesStore').getLanguage(),
}),
);
AlertBanner.propTypes = {
alerts: PropTypes.array.isRequired,
alerts: PropTypes.arrayOf(alertShape).isRequired,
linkAddress: PropTypes.string.isRequired,
language: PropTypes.string.isRequired,
};

AlertBanner.contextTypes = {
config: PropTypes.object.isRequired,
config: configShape.isRequired,
};
export default connectedComponent;
export default AlertBanner;
149 changes: 72 additions & 77 deletions app/component/AlertList.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,35 @@ import PropTypes from 'prop-types';
import React from 'react';
import { FormattedMessage } from 'react-intl';

import RouteAlertsRow from './RouteAlertsRow';
import { createUniqueAlertList } from '../util/alertUtils';
import AlertRow from './AlertRow';
import {
alertCompare,
getEntitiesOfType,
isAlertValid,
} from '../util/alertUtils';
import { alertShape } from '../util/shapes';
import withBreakpoint from '../util/withBreakpoint';
import { getRouteMode } from '../util/modeUtils';
import { AlertEntityType, AlertSeverityLevelType } from '../constants';

const AlertList = ({
cancelations,
currentTime,
disableScrolling,
showExpired,
serviceAlerts,
showRouteNameLink,
showLinks,
breakpoint,
onClickLink,
}) => {
const groupedAlerts =
createUniqueAlertList(
serviceAlerts,
cancelations,
currentTime,
showExpired,
) || [];
const validAlerts = serviceAlerts.filter(alert =>
isAlertValid(alert, currentTime),
);
const validCancelations = cancelations.filter(cancelation =>
isAlertValid(cancelation, currentTime),
);

if (groupedAlerts.length === 0) {
if (validAlerts.length === 0 && validCancelations.length === 0) {
return (
<div className="stop-no-alerts-container" tabIndex="0" aria-live="polite">
<div className="no-alerts-container" tabIndex="0" aria-live="polite">
<FormattedMessage
id="disruption-info-no-alerts"
defaultMessage="No known disruptions or diversions."
Expand All @@ -38,107 +42,98 @@ const AlertList = ({
);
}

// Cancelations should be between non-info alerts and info alerts
const alertsSorted = [
...validAlerts
.filter(alert => alert.alertSeverityLevel !== AlertSeverityLevelType.Info)
.sort(alertCompare),
...validCancelations.sort(alertCompare),
...validAlerts
.filter(alert => alert.alertSeverityLevel === AlertSeverityLevelType.Info)
.sort(alertCompare),
];

return (
<div className="route-alerts-content-wrapper">
<div className="alerts-content-wrapper">
<div
className={cx('route-alerts-list-wrapper', {
className={cx('alerts-list-wrapper', {
'bp-large': breakpoint === 'large',
})}
aria-live="polite"
>
<div
className={cx('route-alerts-list', {
className={cx('alerts-list', {
'momentum-scroll': !disableScrolling,
})}
>
{groupedAlerts.map(
{alertsSorted.map(
(
{
description,
expired,
header,
route: { color, mode, type, shortName, routeGtfsId } = {},
severityLevel,
stop: { code, vehicleMode, stopGtfsId, nameAndCode } = {},
url,
validityPeriod: { startTime, endTime },
source,
alertDescriptionText,
alertHeaderText,
entities,
alertSeverityLevel,
alertUrl,
effectiveStartDate,
effectiveEndDate,
feed,
},
i,
) => (
<RouteAlertsRow
color={color ? `#${color}` : null}
currentTime={currentTime}
description={description}
endTime={endTime}
entityIdentifier={shortName || nameAndCode || code}
entityMode={
(mode && getRouteMode({ mode, type })) ||
(vehicleMode && vehicleMode.toLowerCase())
}
entityType={
(shortName && 'route') || ((nameAndCode || code) && 'stop')
}
expired={expired}
header={header}
key={`alert-${shortName}-${severityLevel}-${i}`} // eslint-disable-line react/no-array-index-key
severityLevel={severityLevel}
startTime={startTime}
url={url}
gtfsIds={routeGtfsId || stopGtfsId}
showRouteNameLink={showRouteNameLink}
source={source}
/>
),
) => {
const entityType =
getEntitiesOfType(entities, AlertEntityType.Stop).length > 0
? 'stop'
: 'route';
return (
<AlertRow
currentTime={currentTime}
description={alertDescriptionText}
endTime={effectiveEndDate}
entities={entities}
feed={feed}
header={alertHeaderText}
// eslint-disable-next-line react/no-array-index-key
key={`alert-${entityType}-${alertSeverityLevel}-${i}`}
severityLevel={alertSeverityLevel}
showLinks={showLinks}
startTime={effectiveStartDate}
url={alertUrl}
index={i}
onClickLink={onClickLink}
/>
);
},
)}
</div>
</div>
</div>
);
};

const alertShape = PropTypes.shape({
description: PropTypes.string,
header: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
route: PropTypes.shape({
color: PropTypes.string,
mode: PropTypes.string,
shortName: PropTypes.string,
}),
severityLevel: PropTypes.string,
stop: PropTypes.shape({
code: PropTypes.string,
vehicleMode: PropTypes.string,
}),
url: PropTypes.string,
validityPeriod: PropTypes.shape({
startTime: PropTypes.number.isRequired,
endTime: PropTypes.number,
}).isRequired,
});

AlertList.propTypes = {
cancelations: PropTypes.arrayOf(alertShape),
currentTime: PropTypes.PropTypes.number.isRequired,
disableScrolling: PropTypes.bool,
serviceAlerts: PropTypes.arrayOf(alertShape),
showExpired: PropTypes.bool,
showRouteNameLink: PropTypes.bool,
showLinks: PropTypes.bool,
breakpoint: PropTypes.string,
onClickLink: PropTypes.func,
};

AlertList.defaultProps = {
cancelations: [],
disableScrolling: false,
serviceAlerts: [],
showExpired: false,
showLinks: false,
breakpoint: undefined,
onClickLink: undefined,
};

const connectedComponent = connectToStores(
withBreakpoint(AlertList),
['TimeStore'],
['TimeStore', 'PreferencesStore'],
context => ({
currentTime: context.getStore('TimeStore').getCurrentTime().unix(),
currentTime: context.getStore('TimeStore').getCurrentTime(),
}),
);

Expand Down
Loading
Loading