diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7206755e7..cfc31658b 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,4 +15,4 @@ jobs: # skip git, yarn, pixel test script and HAR file, and all i18n resources. # Also, the a11y test file has a false positive and the ignore list does not work # see https://github.com/opentripplanner/otp-react-redux/pull/436/checks?check_run_id=3369380014 - skip: ./.git,yarn.lock,./a11y/a11y.test.js,./a11y/mocks,./percy/percy.test.js,./percy/mock.har,./i18n + skip: ./.git,yarn.lock,./a11y/a11y.test.js,./a11y/mocks,./percy/percy.test.js,./percy/mock.har,./i18n,./__tests__/mocks diff --git a/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap b/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap index 411b2ec9d..b4051bdf3 100644 --- a/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap +++ b/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap @@ -1250,7 +1250,7 @@ exports[`components > viewers > stop viewer should render countdown times after
viewers > stop viewer should render countdown times for st

- viewers > stop viewer should render countdown times for st }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
-
  • -
    - - + - viewers > stop viewer should render countdown times for st } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render countdown times for st } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render countdown times for st iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - common.time.tripDurationFormat - - + /> + + + + -
      - -
    2. -
    -
  • - -
    -
    + + + + + common.time.tripDurationFormat + + + +
    + + + + + +
    viewers > stop viewer should render times after midnight w

    - viewers > stop viewer should render times after midnight w "mode": "BUS", "operator": undefined, "shortName": "20", - "sortOrder": 2600, - "sortOrderSet": true, - "type": 3, - } - } - showOperatorLogo={false} - stopTimes={ - Array [ - Object { - "arrivalDelay": 0, - "blockId": "2041", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 89460, - "realtimeDeparture": 89460, - "realtimeState": "SCHEDULED", - "scheduledArrival": 89460, - "scheduledDeparture": 89460, - "serviceDay": 1565161200, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230375", - }, - Object { - "arrivalDelay": 0, - "blockId": "2043", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 93120, - "realtimeDeparture": 93120, - "realtimeState": "SCHEDULED", - "scheduledArrival": 93120, - "scheduledDeparture": 93120, - "serviceDay": 1565161200, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230376", - }, - Object { - "arrivalDelay": 0, - "blockId": "2049", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 96780, - "realtimeDeparture": 96780, - "realtimeState": "SCHEDULED", - "scheduledArrival": 96780, - "scheduledDeparture": 96780, - "serviceDay": 1565161200, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230377", - }, - Object { - "arrivalDelay": 0, - "blockId": "2067", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 13980, - "realtimeDeparture": 13980, - "realtimeState": "SCHEDULED", - "scheduledArrival": 13980, - "scheduledDeparture": 13980, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230305", - }, - Object { - "arrivalDelay": 0, - "blockId": "2034", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 17580, - "realtimeDeparture": 17580, - "realtimeState": "SCHEDULED", - "scheduledArrival": 17580, - "scheduledDeparture": 17580, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230306", - }, - Object { - "arrivalDelay": 0, - "blockId": "2069", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 19020, - "realtimeDeparture": 19020, - "realtimeState": "SCHEDULED", - "scheduledArrival": 19020, - "scheduledDeparture": 19020, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230307", - }, - ] - } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } - > - +
  • -
  • -
    - - + - viewers > stop viewer should render times after midnight w } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render times after midnight w } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render times after midnight w iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 12:51 AM - - - -
      - -
    2. -
    3. - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render times after midnight w } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render times after midnight w iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + - - 1:52 AM - - - -
    - -
  • -
  • - + + 1:52 AM + + + + + +
  • +
  • + +
    -
    - viewers > stop viewer should render times after midnight w } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render times after midnight w iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 2:53 AM - - - -
    - -
  • - - - - - + 2:53 AM + + + + + + + + + +
    viewers > stop viewer should render with OTP transit index

    - viewers > stop viewer should render with OTP transit index "realtimeDeparture": 66668, "realtimeState": "SCHEDULED", "scheduledArrival": 66668, - "scheduledDeparture": 66668, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230360", - }, - Object { - "arrivalDelay": 0, - "blockId": "2048", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 67628, - "realtimeDeparture": 67628, - "realtimeState": "SCHEDULED", - "scheduledArrival": 67628, - "scheduledDeparture": 67628, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230361", - }, - Object { - "arrivalDelay": 0, - "blockId": "2036", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 70028, - "realtimeDeparture": 70028, - "realtimeState": "SCHEDULED", - "scheduledArrival": 70028, - "scheduledDeparture": 70028, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230363", - }, - Object { - "arrivalDelay": 0, - "blockId": "2071", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 72436, - "realtimeDeparture": 72436, - "realtimeState": "SCHEDULED", - "scheduledArrival": 72436, - "scheduledDeparture": 72436, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230365", - }, - ] - } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } - > - +
  • -
  • -
    - - + - viewers > stop viewer should render with OTP transit index } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:00 PM - - - -
      - -
    2. -
    3. - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:15 PM - - - -
    - -
  • -
  • - + + + + +
  • +
  • + +
    -
    - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:31 PM - - - -
    - -
  • - - - - - + 6:31 PM + + + + + + + + + +

    viewers > stop viewer should render with OTP transit index

    - viewers > stop viewer should render with OTP transit index "timepoint": true, "tripId": "TriMet:9238190", }, - Object { - "arrivalDelay": 0, - "blockId": "9474", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 56880, - "realtimeDeparture": 56880, - "realtimeState": "SCHEDULED", - "scheduledArrival": 56880, - "scheduledDeparture": 56880, - "serviceDay": 1565074800, - "stopCount": 40, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238194", - }, - Object { - "arrivalDelay": 0, - "blockId": "9470", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 54720, - "realtimeDeparture": 54720, - "realtimeState": "SCHEDULED", - "scheduledArrival": 54720, - "scheduledDeparture": 54720, - "serviceDay": 1565161200, - "stopCount": 34, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238190", - }, - Object { - "arrivalDelay": 0, - "blockId": "9470", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 54720, - "realtimeDeparture": 54720, - "realtimeState": "SCHEDULED", - "scheduledArrival": 54720, - "scheduledDeparture": 54720, - "serviceDay": 1565247600, - "stopCount": 34, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238190", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + + + 94 + + + + + + Sherwood + +
    +
      +
    1. + +
      + + + + + + + + + + + + + + + + + common.daysOfWeek.tuesday + + + + + + + + 2:28 PM + + + +
      +
      +
    2. +
    3. + +
      - - 94 + + + + + + + - - - - - Sherwood - -
      -
        -
      1. - + + + + + + common.daysOfWeek.tuesday + + + + + + + + 3:02 PM + + + +
    + +
  • +
  • + +
    -
    - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 2:28 PM - - - -
    - -
  • -
  • - + + +
  • + + + + + +
    + +
  • +
    +
    + + + + + 94 + + + + + + King City + +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - - - 3:02 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 3:12 PM - - - -
      -
      -
    4. -
    -
    -
  • -
    -
    - + + + + + + + + + + viewers > stop viewer should render with OTP transit index Array [ Object { "arrivalDelay": 0, - "blockId": "9472", + "blockId": "3668", "departureDelay": 0, - "headsign": "King City", + "headsign": "Tualatin Park & Ride", "realtime": false, - "realtimeArrival": 55320, - "realtimeDeparture": 55320, + "realtimeArrival": 58260, + "realtimeDeparture": 58260, "realtimeState": "SCHEDULED", - "scheduledArrival": 55320, - "scheduledDeparture": 55320, + "scheduledArrival": 58260, + "scheduledDeparture": 58260, "serviceDay": 1565074800, - "stopCount": 23, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238192", - }, - Object { - "arrivalDelay": 0, - "blockId": "9472", - "departureDelay": 0, - "headsign": "King City", - "realtime": false, - "realtimeArrival": 55320, - "realtimeDeparture": 55320, - "realtimeState": "SCHEDULED", - "scheduledArrival": 55320, - "scheduledDeparture": 55320, - "serviceDay": 1565161200, - "stopCount": 23, + "stopCount": 63, "stopId": "TriMet:715", "stopIndex": 0, "timepoint": true, - "tripId": "TriMet:9238192", + "tripId": "TriMet:9231858", }, Object { "arrivalDelay": 0, - "blockId": "9472", + "blockId": "3670", "departureDelay": 0, - "headsign": "King City", + "headsign": "Tualatin Park & Ride", "realtime": false, - "realtimeArrival": 55320, - "realtimeDeparture": 55320, + "realtimeArrival": 61740, + "realtimeDeparture": 61740, "realtimeState": "SCHEDULED", - "scheduledArrival": 55320, - "scheduledDeparture": 55320, - "serviceDay": 1565247600, - "stopCount": 23, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238192", - }, - ] - } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } - > - +
  • -
  • -
    - - + - + 36 + + + + + + Tualatin Park & Ride + +
    +
      +
    1. + +
      + - 94 + + + + + + + - - - - - King City - -
      -
        -
      1. - + + + + + + common.daysOfWeek.tuesday + + + + + + + + 4:11 PM + + + +
    + +
  • +
  • + +
    -
    - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 3:22 PM - - - -
    - -
  • -
  • - + + + + +
  • + + + +
    +

    + + + common.daysOfWeek.wednesday + + +

    + +
  • +
    +
    + + + -
    + 94 + + + + + + King City + +
    +
      +
    1. + +
      + - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.wednesday - - - - - - - - 3:22 PM - - + /> + + + + -
      -
      -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.thursday - - - - - - + + + + + + - - 3:22 PM - - - -
      -
      -
    4. -
    -
    -
  • -
    -
    - + + + + + + + + + + viewers > stop viewer should render with OTP transit index showOperatorLogo={false} stopTimes={ Array [ - Object { - "arrivalDelay": 0, - "blockId": "3668", - "departureDelay": 0, - "headsign": "Tualatin Park & Ride", - "realtime": false, - "realtimeArrival": 58260, - "realtimeDeparture": 58260, - "realtimeState": "SCHEDULED", - "scheduledArrival": 58260, - "scheduledDeparture": 58260, - "serviceDay": 1565074800, - "stopCount": 63, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9231858", - }, - Object { - "arrivalDelay": 0, - "blockId": "3670", - "departureDelay": 0, - "headsign": "Tualatin Park & Ride", - "realtime": false, - "realtimeArrival": 61740, - "realtimeDeparture": 61740, - "realtimeState": "SCHEDULED", - "scheduledArrival": 61740, - "scheduledDeparture": 61740, - "serviceDay": 1565074800, - "stopCount": 63, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9231860", - }, Object { "arrivalDelay": 0, "blockId": "3668", @@ -12261,179 +11402,57 @@ exports[`components > viewers > stop viewer should render with OTP transit index }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - -
  • -
    +
    -
    - - + - viewers > stop viewer should render with OTP transit index } title="36" > - - 36 - - - - - - Tualatin Park & Ride - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - - - 4:11 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 5:09 PM - - - -
      -
      -
    4. -
    5. - + + +
    + +
  • + + + +
    +

    + + + common.daysOfWeek.thursday + + +

    + +
  • +
    +
    + + + + + 94 + + + + + + King City + +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 4:11 PM - - - -
      - -
    2. -
    -
    -
  • -
    -
    + 3:22 PM + + + + + + + + + +
    viewers > stop viewer should render with TriMet transit in

    - viewers > stop viewer should render with TriMet transit in "realtimeDeparture": 65759, "realtimeState": "SCHEDULED", "scheduledArrival": 65759, - "scheduledDeparture": 65759, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230359", - }, - Object { - "arrivalDelay": 0, - "blockId": "2047", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 66668, - "realtimeDeparture": 66668, - "realtimeState": "SCHEDULED", - "scheduledArrival": 66668, - "scheduledDeparture": 66668, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230360", - }, - Object { - "arrivalDelay": 0, - "blockId": "2036", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 70028, - "realtimeDeparture": 70028, - "realtimeState": "SCHEDULED", - "scheduledArrival": 70028, - "scheduledDeparture": 70028, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230363", - }, - Object { - "arrivalDelay": 0, - "blockId": "2071", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 72436, - "realtimeDeparture": 72436, - "realtimeState": "SCHEDULED", - "scheduledArrival": 72436, - "scheduledDeparture": 72436, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230365", - }, - ] - } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } - > - +
  • -
  • -
    - - + - viewers > stop viewer should render with TriMet transit in } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 5:45 PM - - - -
      - -
    2. -
    3. - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:00 PM - - - -
    - -
  • -
  • - + + + + +
  • +
  • + +
    -
    - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:15 PM - - - -
    - -
  • - - - - - + 6:15 PM + + + + + + + + + +
    stop-times', () => { + describe('sortAndGroupStopTimesByDay', () => { + it('should sort and group stop times by day', () => { + const stopTimesByPatternByDay = groupAndSortStopTimesByPatternByDay( + stopData, + now, + daysAhead, + 3 + ) + + function getPatternDays(id: string) { + return stopTimesByPatternByDay + .filter((p) => p.id === id) + .map((p) => p.day) + } + + // Stop time data has 4 patterns aggregating to 3 final destinations. + // (This method assumes that the routing is roughly the same for patterns + // that have the same destination.) + const headsigns = new Set( + stopTimesByPatternByDay.map((st) => st.pattern.headsign) + ) + expect(headsigns).toEqual( + new Set(['Northgate', 'Stadium', 'University of Washington']) + ) + + // The pattern to UWA should be included twice, once per service day outside of today. + expect(getPatternDays('40:100479-University of Washington')).toEqual([ + 1695279600, 1695366000 + ]) + + // Patterns to Stadium (last trips in the evening) should be for "today"'s service day. + expect(getPatternDays('40:100479-Stadium')).toEqual([1695193200]) + + // No next-day pattern to Northgate should be returned (same-day depatures exist). + expect(getPatternDays('40:100479-Northgate')).toEqual([1695193200]) + + // Patterns should be sorted by day, then by departure time. + for (let i = 1; i < stopTimesByPatternByDay.length; i++) { + const prevPattern = stopTimesByPatternByDay[i - 1] + const thisPattern = stopTimesByPatternByDay[i] + expect( + prevPattern.day < thisPattern.day || + (prevPattern.day === thisPattern.day && + prevPattern.times[0].realtimeDeparture <= + thisPattern.times[0].realtimeDeparture) + ).toBe(true) + } + }) + }) +}) diff --git a/i18n/es.yml b/i18n/es.yml index 8ddaa7901..63e8913cd 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:" @@ -53,11 +52,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: >- @@ -241,12 +238,12 @@ components: modeSelectorLabel: Seleccione un modo de transporte BatchSettings: destination: destino + invalidModeSelection: >- + No se puede planificar un viaje utilizando los modos seleccionados. Prueba + a incluir el transporte publico en la selección de modos. origin: origen planTripTooltip: Planificar viaje - validationMessage: "Por favor, defina los siguientes campos para planificar un - viaje: {issues}" - invalidModeSelection: No se puede planificar un viaje utilizando los modos seleccionados. - Prueba a incluir el transporte publico en la selección de modos. + validationMessage: "Por favor, defina los siguientes campos para planificar un viaje: {issues}" BeforeSignInScreen: mainTitle: Iniciando sesión message: > @@ -538,6 +535,8 @@ components: Otro viaje guardado ya utiliza este nombre. Por favor, elija un nombre diferente. tripNameRequired: Por favor, introduzca el nombre del viaje. + SequentialPaneDisplay: + stepNumber: Paso {step} de {total} SessionTimeout: body: >- Su sesión expirará en un minuto. Pulse 'Continuar sesión' para mantener su @@ -545,8 +544,7 @@ components: header: ¡La sesión está a punto de terminar! keepSession: Continuar sesión 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: @@ -609,19 +607,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 @@ -781,8 +776,6 @@ components: switcher: Botón de cambio WelcomeScreen: prompt: ¿A donde quiere ir? - SequentialPaneDisplay: - stepNumber: Paso {step} de {total} config: accessModes: bicycle: Tránsito + Bicicleta Personal diff --git a/i18n/vi.yml b/i18n/vi.yml index ba852fde4..e31f08271 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -5,8 +5,7 @@ actions: callQuerySaveError: "Lỗi khi lưu trữ các truy vấn cuộc gọi: {err}" callSaveError: "Không thể lưu cuộc gọi: {err}" checkSessionError: "Lỗi khi thiết lập phiên ủy quyền: {err}" - couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy - vấn. + couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy vấn. fetchCallsError: "Lỗi khi tìm nạp cuộc gọi: {err}" queryFetchError: "Lỗi khi tìm nạp các truy vấn: {err}" fieldTrip: @@ -29,16 +28,14 @@ actions: Không thể lưu kế hoạch chuyến đi: Không thể lưu kế hoạch chuyến đi này do thiếu sức chứa trên một hoặc nhiều xe. Vui lòng lên kế hoạch lại chuyến đi của bạn. - maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết - quả hợp lệ + maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết quả hợp lệ saveItinerariesError: "Không lưu được hành trình: {err}" setDateError: "Lỗi khi cài đặt ngày:" setGroupSizeError: "Lỗi khi cài đặt kích thước nhóm:" setPaymentError: "Lỗi khi cài đặt thông tin thanh toán:" setRequestStatusError: "Lỗi khi cài đặt trạng thái yêu cầu:" location: - geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt - của bạn + geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt của bạn unknownPositionError: Lỗi không xác định khi tìm vị trí map: currentLocation: (Vị trí hiện tại) @@ -49,8 +46,7 @@ actions: confirmDeletePlace: Bạn có muốn loại bỏ nơi này không? emailVerificationResent: Thông báo xác minh email đã được gửi lại. genericError: "Phát sinh lỗi: {err}" - itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là - có thể. + itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là có thể. preferencesSaved: Những sở thích của bạn đã được lưu lại. smsInvalidCode: Mã bạn nhập không hợp lệ. Vui lòng thử lại. smsResendThrottled: >- @@ -160,14 +156,12 @@ common: {} other {# giây}} components: A11yPrefs: - accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc - định + accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc định AccountSetupFinishPane: message: Bạn đã sẵn sàng để bắt đầu lên kế hoạch cho các chuyến đi của bạn. AddPlaceButton: addPlace: Thêm địa điểm - needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung - gian + needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung gian tooManyPlaces: Địa điểm trung gian tối đa đạt được AdvancedOptions: bannedRoutes: Chọn các tuyến đường bị cấm… @@ -278,8 +272,7 @@ components: editPlaceGeneric: Chỉnh sửa vị trí invalidAddress: Vui lòng cài đặt một vị trí cho nơi này. invalidName: Vui lòng nhập tên cho nơi này. - nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên - khác. + nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên khác. placeNotFound: Không tìm thấy địa điểm placeNotFoundDescription: Xin lỗi, địa điểm được yêu cầu không được tìm thấy. FormNavigationButtons: @@ -521,22 +514,17 @@ components: travelingAt: di chuyển với tốc độ {milesPerHour} vehicleName: Phương tiện giao thông {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong - tuần… + checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong tuần… selectAtLeastOneDay: Vui lòng chọn ít nhất một ngày để theo dõi. tripDaysPrompt: Bạn thực hiện chuyến đi này vào những ngày nào? - tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong - tuần như đã nêu ở trên. + tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong tuần như đã nêu ở trên. tripNamePrompt: "Vui lòng cung cấp tên cho chuyến đi này:" tripNotAvailableOnDay: Chuyến đi không có sẵn vào {repeatedDay} - unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những - thay đổi sẽ bị mất. - unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó - sẽ bị mất. + unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những thay đổi sẽ bị mất. + unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó sẽ bị mất. TripNotificationsPane: advancedSettings: Cài đặt nâng cao - altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến - nghị + altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến nghị delaysAboveThreshold: Có sự chậm trễ hoặc gián đoạn của hơn howToReceiveAlerts: > Để nhận thông báo cho các chuyến đi đã lưu của bạn, bật thông báo trong @@ -545,8 +533,7 @@ components: notificationsTurnedOff: Thông báo được tắt cho tài khoản của bạn. notifyViaChannelWhen: "Thông báo cho tôi qua {channel} khi:" oneHour: 1 tiếng - realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình - của tôi + realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình của tôi timeBefore: "{time} trước" TripStatus: alerts: "{alerts, plural, one {# cảnh báo!} other {# cảnh báo!}}" @@ -559,8 +546,7 @@ components: earlyHeading: >- Chuyến đi đang diễn ra và sẽ đến sớm hơn {formattedDuration} so với dự kiến! - noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực - có sẵn). + noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực có sẵn). onTimeHeading: Chuyến đi đang được tiến hành và đúng giờ. base: lastCheckedDefaultText: Thời gian được kiểm tra lần cuối không xác định @@ -603,8 +589,7 @@ components: tripStartIsEarly: >- Thời gian bắt đầu chuyến đi đang diễn ra sớm hơn ${duration} so với dự kiến! - tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời - gian thực). + tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời gian thực). tripStartsSoonOnTime: Chuyến đi đang bắt đầu sớm và sắp đúng giờ. TripSummary: arriveAt: "Đến nơi " diff --git a/lib/components/util/types.ts b/lib/components/util/types.ts index 8988c7126..664008344 100644 --- a/lib/components/util/types.ts +++ b/lib/components/util/types.ts @@ -24,13 +24,12 @@ export interface BikeRental { stations: any[] } -// FIXME: incomplete export interface StopTime { - departureDelay: number + departureDelay?: number headsign: string pattern: Pattern - realtimeDeparture: boolean - realtimeState: string + realtimeDeparture?: boolean + realtimeState?: string times: Time[] } @@ -66,6 +65,17 @@ export interface Time { tripId: string } +export interface PatternStopTimes { + id: string + pattern: Pattern + route: Route + times: Time[] +} + +export interface PatternDayStopTimes extends PatternStopTimes { + day: number +} + export interface VehicleRental { errorsByNetwork: { [key: string]: { message?: string; severity?: string } } systemInformationDataByNetwork: { diff --git a/lib/components/viewers/live-stop-times.tsx b/lib/components/viewers/live-stop-times.tsx index e8a772024..f07bc7662 100644 --- a/lib/components/viewers/live-stop-times.tsx +++ b/lib/components/viewers/live-stop-times.tsx @@ -1,4 +1,3 @@ -import { addDays, isBefore } from 'date-fns' import { format, utcToZonedTime } from 'date-fns-tz' import { FormattedMessage, @@ -12,14 +11,9 @@ import coreUtils from '@opentripplanner/core-utils' import isSameDay from 'date-fns/isSameDay' import React, { Component } from 'react' -import { - getRouteIdForPattern, - getStopTimesByPattern, - patternComparator, - routeIsValid, - stopTimeComparator -} from '../../util/viewer' +import { groupAndSortStopTimesByPatternByDay } from '../../util/stop-times' import { IconWithText } from '../util/styledIcon' +import { StopData } from '../util/types' import FormattedDayOfWeek from '../util/formatted-day-of-week' import SpanWithSpace from '../util/span-with-space' @@ -39,18 +33,16 @@ type Props = { findStopTimesForStop: ({ stopId }: { stopId: string }) => void homeTimezone: string intl: IntlShape - nearbyStops: any // TODO: shared types + nearbyStops: string[] setHoveredStop: (stopId: string) => void showNearbyStops: boolean showOperatorLogo?: boolean - // TODO: shared types - stopData: any + stopData: StopData stopViewerArriving: React.ReactNode // TODO: shared types stopViewerConfig: any toggleAutoRefresh: (enable: boolean) => void - // TODO: shared types - transitOperators: any + transitOperators: TransitOperator[] viewedStop: { stopId: string } } @@ -164,76 +156,45 @@ class LiveStopTimes extends Component { } = this.props const { spin } = this.state const userTimezone = getUserTimezone() - // construct a lookup table mapping pattern (e.g. 'ROUTE_ID-HEADSIGN') to - // an array of stoptimes - const stopTimesByPattern = getStopTimesByPattern(stopData) const now = utcToZonedTime(Date.now(), homeTimezone) // Time range is set in seconds, so convert to days - const timeRange = stopViewerConfig.timeRange / 86400 || 2 + const daysAhead = stopViewerConfig.timeRange / 86400 || 2 const refreshButtonText = intl.formatMessage({ id: 'components.LiveStopTimes.refresh' }) - const routeTimes = Object.values(stopTimesByPattern) - .filter( - ({ pattern, route, times }) => - times && - times.length !== 0 && - routeIsValid(route, getRouteIdForPattern(pattern)) - ) - .sort(patternComparator) - .map((route) => { - const sortedTimes = route.times - .concat() - ?.sort(stopTimeComparator) - // filter any times according to time range set in config. - .filter((time: any, i: number, array: Array) => { - const departureTime = time.serviceDay + time.realtimeDeparture - return isBefore(departureTime, addDays(now, timeRange)) - }) - const { serviceDay } = sortedTimes[0] - return { - ...route, - day: serviceDay || null, - times: sortedTimes - } - }) - // if the time range filter removes all times, remove route - .filter(({ times }) => times.length !== 0) + const routeTimes = groupAndSortStopTimesByPatternByDay( + stopData, + now, + daysAhead, + stopViewerConfig.numberOfDepartures + ) return ( <>
      - {routeTimes.map((time: any, index: number) => { - const { id, pattern, route, times } = time - return ( - - {((index > 0 && - !isSameDay( - time.day * 1000, - routeTimes[index - 1]?.day * 1000 - )) || - (index === 0 && !isSameDay(now, time.day * 1000))) && - this.renderDay(homeTimezone, time.day, now)} - o.agencyId === route.agencyId - ) - }} - showOperatorLogo={showOperatorLogo} - stopTimes={times} - stopViewerArriving={stopViewerArriving} - stopViewerConfig={stopViewerConfig} - /> - - ) - })} + {routeTimes.map(({ day, id, pattern, route, times }, index) => ( + + {((index > 0 && + !isSameDay(day * 1000, routeTimes[index - 1]?.day * 1000)) || + (index === 0 && !isSameDay(now, day * 1000))) && + this.renderDay(homeTimezone, day, now)} + o.agencyId === route.agencyId + ) + }} + showOperatorLogo={showOperatorLogo} + stopTimes={times} + /> + + ))}
    {/* Auto update controls for realtime arrivals */} diff --git a/lib/components/viewers/next-arrival-for-pattern.tsx b/lib/components/viewers/next-arrival-for-pattern.tsx index 2fceb4cf8..bed35582c 100644 --- a/lib/components/viewers/next-arrival-for-pattern.tsx +++ b/lib/components/viewers/next-arrival-for-pattern.tsx @@ -21,7 +21,7 @@ type Props = { pattern: Pattern // Not the true operator type, but the one that's used here // It is annoying to shoehorn the operator in here like this, but passing - // it in indvidually would cause a situation where a list of routes + // it in individually would cause a situation where a list of routes // needs to be matched up with a list of operators route: Route & { operator?: { colorMode?: string } } routeColor: string diff --git a/lib/components/viewers/pattern-row.tsx b/lib/components/viewers/pattern-row.tsx index 0b4084251..cc1765974 100644 --- a/lib/components/viewers/pattern-row.tsx +++ b/lib/components/viewers/pattern-row.tsx @@ -1,6 +1,5 @@ import { getMostReadableTextColor } from '@opentripplanner/core-utils/lib/route' -import { injectIntl, IntlShape } from 'react-intl' -import React, { Component } from 'react' +import React, { useContext } from 'react' import type { Route, TransitOperator } from '@opentripplanner/types' import { ComponentContext } from '../../util/contexts' @@ -16,107 +15,81 @@ import OperatorLogo from '../util/operator-logo' import StopTimeCell from './stop-time-cell' type Props = { - homeTimezone?: any - intl: IntlShape + homeTimezone?: string pattern: Pattern route: Route & { operator?: TransitOperator & { colorMode?: string } } showOperatorLogo?: boolean stopTimes: Time[] - stopViewerArriving: React.ReactNode - stopViewerConfig: { numberOfDepartures: number } } -type State = { expanded: boolean } + /** * Represents a single pattern row for displaying arrival times in the stop * viewer. */ -class PatternRow extends Component { - constructor(props: Props) { - super(props) - this.state = { expanded: false } - } - - static contextType = ComponentContext +const PatternRow = ({ + homeTimezone, + pattern, + route, + showOperatorLogo, + stopTimes +}: Props): JSX.Element | null => { + // @ts-expect-error FIXME: No type on ComponentContext + const { RouteRenderer: CustomRouteRenderer } = useContext(ComponentContext) + const RouteRenderer = CustomRouteRenderer || DefaultRouteRenderer - _toggleExpandedView = () => { - this.setState({ expanded: !this.state.expanded }) + const hasStopTimes = stopTimes && stopTimes.length > 0 + if (!hasStopTimes) { + return null } - render() { - const { RouteRenderer: CustomRouteRenderer } = this.context - const RouteRenderer = CustomRouteRenderer || DefaultRouteRenderer - const { - homeTimezone, - pattern, - route, - showOperatorLogo, - stopTimes, - stopViewerConfig - } = this.props + const routeName = route.shortName ? route.shortName : route.longName + const routeColor = getRouteColorBasedOnSettings(route.operator, route) - // sort stop times by next departure - let sortedStopTimes: Time[] = [] - const hasStopTimes = stopTimes && stopTimes.length > 0 - if (hasStopTimes) { - sortedStopTimes = stopTimes - // We request only x departures per pattern, but the patterns are merged - // according to shared headsigns, so we need to slice the stop times - // here as well to ensure only x times are shown per route/headsign combo. - .slice(0, stopViewerConfig.numberOfDepartures) - } else { - // Do not render pattern row if it has no stop times. - return null - } - - const routeName = route.shortName ? route.shortName : route.longName - const routeColor = getRouteColorBasedOnSettings(route.operator, route) - - return ( -
  • - {/* header row */} -
    - {/* route name */} -
    - - {showOperatorLogo && } - - - {pattern.headsign} -
    - {/* next departure preview */} - {hasStopTimes && ( -
      - {[0, 1, 2].map( - (index) => - sortedStopTimes?.[index] && ( -
    1. - -
    2. - ) + return ( +
    3. + {/* header row */} +
      + {/* route name */} +
      + + {showOperatorLogo && } + - )} + leg={generateFakeLegForRouteRenderer(route, true)} + style={{ fontSize: routeNameFontSize(routeName) }} + /> + + {pattern.headsign}
      -
    4. - ) - } + {/* next departure preview (only shows up to 3 entries) */} + {hasStopTimes && ( +
        + {[0, 1, 2].map( + (index) => + stopTimes?.[index] && ( +
      1. + +
      2. + ) + )} +
      + )} +
    +
  • + ) } -export default injectIntl(PatternRow) +export default PatternRow diff --git a/lib/util/arrays.ts b/lib/util/arrays.ts new file mode 100644 index 000000000..d5429ae6c --- /dev/null +++ b/lib/util/arrays.ts @@ -0,0 +1,12 @@ +/** Retrieves a key from a dictionary or adds it with the desired value if absent. */ +export function getOrPutEntry( + entries: Record, + key: K, + newValue: (newKey: K) => V +): V { + let entry = entries[key] + if (!entry) { + entries[key] = entry = newValue(key) + } + return entry +} diff --git a/lib/util/stop-times.ts b/lib/util/stop-times.ts new file mode 100644 index 000000000..f58deae2f --- /dev/null +++ b/lib/util/stop-times.ts @@ -0,0 +1,82 @@ +import { addDays, isBefore } from 'date-fns' + +import { + PatternDayStopTimes, + PatternStopTimes, + StopData, + Time +} from '../components/util/types' + +import { getOrPutEntry } from './arrays' +import { + getRouteIdForPattern, + getStopTimesByPattern, + patternComparator, + routeIsValid, + stopTimeComparator +} from './viewer' + +function hasValidTimesAndRoute({ + pattern, + route, + times +}: PatternStopTimes): boolean { + return ( + times && + times.length !== 0 && + routeIsValid(route, getRouteIdForPattern(pattern)) + ) +} + +/** Filter any times according to time range set in config. */ +function isWithinDaysAhead(time: Time, now: Date, daysAhead: number) { + const departureTime = time.serviceDay + time.realtimeDeparture + return isBefore(departureTime, addDays(now, daysAhead)) +} + +/** Helper to sort and group stop times by pattern by service day */ +export function groupAndSortStopTimesByPatternByDay( + stopData: StopData, + now: Date, + daysAhead: number, + numberOfDepartures: number +): PatternDayStopTimes[] { + // construct a lookup table mapping pattern (e.g. 'ROUTE_ID-HEADSIGN') to + // an array of stoptimes + const stopTimesByPattern = getStopTimesByPattern(stopData) as Record< + string, + PatternStopTimes + > + + return ( + Object.values(stopTimesByPattern) + .filter(hasValidTimesAndRoute) + .map((pattern) => + pattern.times + .concat([]) + .sort(stopTimeComparator) + .filter((time) => isWithinDaysAhead(time, now, daysAhead)) + // remove excess departure times + .slice(0, numberOfDepartures) + // collect times by pattern by day + // (every pattern returned has stop times within the days ahead.) + .reduce>((days, t) => { + const { serviceDay } = t + const patternDay = getOrPutEntry(days, serviceDay, (day) => ({ + ...pattern, + day, + times: [] + })) + patternDay.times.push(t) + return days + }, {}) + ) + // Concatenate all resulting patterns + .reduce( + (result, cur) => result.concat(Object.values(cur)), + [] + ) + // Sort route times by service day then realtime departure + .sort(patternComparator) + ) +} diff --git a/lib/util/viewer.js b/lib/util/viewer.js index bdb27b3cb..9f1f9ef9d 100644 --- a/lib/util/viewer.js +++ b/lib/util/viewer.js @@ -40,7 +40,7 @@ function excludeLastStop(stopTime) { * Checks that the given route object from an OTP pattern is valid. * If it is not, logs a warning message. * - * FIXME: there is currently a bug with the alernative transit index + * FIXME: there is currently a bug with the alternative transit index * where routes are not associated with the stop if the only stoptimes * for the stop are drop off only. See https://github.com/ibi-group/trimet-mod-otp/issues/217 *