Skip to content

Commit 3b0ac43

Browse files
authored
Merge pull request #344 from opentripplanner/enhance-auto-plan
Clarify strategies for auto-plan
2 parents c1ee35e + 3d4a061 commit 3b0ac43

File tree

4 files changed

+98
-32
lines changed

4 files changed

+98
-32
lines changed

Diff for: __tests__/reducers/__snapshots__/create-otp-reducer.js.snap

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ exports[`lib > reducers > create-otp-reducer should be able to create the initia
44
Object {
55
"activeSearchId": 0,
66
"config": Object {
7-
"autoPlan": false,
7+
"autoPlan": Object {
8+
"default": "ONE_LOCATION_CHANGED",
9+
"mobile": "BOTH_LOCATIONS_CHANGED",
10+
},
811
"debouncePlanTimeMs": 0,
912
"homeTimezone": "America/Los_Angeles",
1013
"language": Object {},

Diff for: example-config.yml

+11
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,22 @@ api:
1515
# lon: -122.71607145667079
1616
# name: Oregon Zoo, Portland, OR
1717

18+
19+
### Define the strategies for how to handle auto-planning of a new trip when
20+
### different query parameters are changes in the form. The default config is
21+
### shown below, but if autoPlan is set to false, auto-plan will never occur.
22+
### Other strategies besides those shown below are: ANY (any changed param will
23+
### cause a re-plan).
24+
# autoPlan:
25+
# mobile: BOTH_LOCATIONS_CHANGED
26+
# default: ONE_LOCATION_CHANGED
27+
1828
### The default query parameters can be overridden be uncommenting this object.
1929
### Note: the override values must be valid values within otp-ui's query-params.js
2030
# defaultQueryParams:
2131
# maxWalkDistance: 3219 # 2 miles in meters
2232

33+
2334
### The persistence setting is used to enable the storage of places (home, work),
2435
### recent searches/places, user overrides, and favorite stops.
2536
### Pick the strategy that best suits your needs.

Diff for: lib/actions/form.js

+79-30
Original file line numberDiff line numberDiff line change
@@ -78,48 +78,50 @@ export function parseUrlQueryString (params = getUrlParams()) {
7878
let debouncedPlanTrip // store as variable here, so it can be reused.
7979
let lastDebouncePlanTimeMs
8080

81+
/**
82+
* This action is dispatched when a change between the old query and new query
83+
* is detected. It handles checks for whether the trip should be replanned
84+
* (based on autoPlan strategies) as well as updating the UI state (esp. for
85+
* mobile).
86+
*/
8187
export function formChanged (oldQuery, newQuery) {
8288
return function (dispatch, getState) {
8389
const otpState = getState().otp
90+
const { config, currentQuery, ui } = otpState
91+
const { autoPlan, debouncePlanTimeMs } = config
8492
const isMobile = coreUtils.ui.isMobile()
85-
93+
const {
94+
fromChanged,
95+
oneLocationChanged,
96+
shouldReplanTrip,
97+
toChanged
98+
} = checkShouldReplanTrip(autoPlan, isMobile, oldQuery, newQuery)
8699
// If departArrive is set to 'NOW', update the query time to current
87-
if (otpState.currentQuery && otpState.currentQuery.departArrive === 'NOW') {
88-
dispatch(settingQueryParam({ time: moment().format(coreUtils.time.OTP_API_TIME_FORMAT) }))
100+
if (currentQuery.departArrive === 'NOW') {
101+
const now = moment().format(coreUtils.time.OTP_API_TIME_FORMAT)
102+
dispatch(settingQueryParam({ time: now }))
89103
}
90-
91-
// Determine if either from/to location has changed
92-
const fromChanged = !isEqual(oldQuery.from, newQuery.from)
93-
const toChanged = !isEqual(oldQuery.to, newQuery.to)
94-
95104
// Only clear the main panel if a single location changed. This prevents
96105
// clearing the panel on load if the app is focused on a stop viewer but a
97106
// search query should also be visible.
98-
const oneLocationChanged = (fromChanged && !toChanged) || (!fromChanged && toChanged)
99107
if (oneLocationChanged) {
100108
dispatch(setMainPanelContent(null))
101109
}
102-
103-
// Clear the current search and return to search screen on mobile when
104-
// either location changes only if not currently on welcome screen (otherwise
105-
// when the current position is auto-set the screen will change unexpectedly).
106-
if (
107-
isMobile &&
108-
(fromChanged || toChanged) &&
109-
otpState.ui.mobileScreen !== MobileScreens.WELCOME_SCREEN
110-
) {
111-
dispatch(clearActiveSearch())
112-
dispatch(setMobileScreen(MobileScreens.SEARCH_FORM))
113-
}
114-
115-
// Check whether a trip should be auto-replanned
116-
const { autoPlan, debouncePlanTimeMs } = otpState.config
117-
const updatePlan =
118-
autoPlan ||
119-
(!isMobile && oneLocationChanged) || // TODO: make autoplan configurable at the parameter level?
120-
(isMobile && fromChanged && toChanged)
121-
if (updatePlan && queryIsValid(otpState)) { // trip plan should be made
122-
// check if debouncing function needs to be (re)created
110+
if (!shouldReplanTrip) {
111+
// If not replanning the trip, clear the current search when either
112+
// location changes.
113+
if (fromChanged || toChanged) {
114+
dispatch(clearActiveSearch())
115+
// Return to search screen on mobile only if not currently on welcome
116+
// screen (otherwise when the current position is auto-set the screen
117+
// will change unexpectedly).
118+
if (ui.mobileScreen !== MobileScreens.WELCOME_SCREEN) {
119+
dispatch(setMobileScreen(MobileScreens.SEARCH_FORM))
120+
}
121+
}
122+
} else if (queryIsValid(otpState)) {
123+
// If replanning trip and query is valid,
124+
// check if debouncing function needs to be (re)created.
123125
if (!debouncedPlanTrip || lastDebouncePlanTimeMs !== debouncePlanTimeMs) {
124126
debouncedPlanTrip = debounce(() => dispatch(routingQuery()), debouncePlanTimeMs)
125127
lastDebouncePlanTimeMs = debouncePlanTimeMs
@@ -128,3 +130,50 @@ export function formChanged (oldQuery, newQuery) {
128130
}
129131
}
130132
}
133+
134+
/**
135+
* Check if the trip should be replanned based on the auto plan strategy,
136+
* whether the mobile view is active, and the old/new queries. Response type is
137+
* an object containing various booleans.
138+
*/
139+
export function checkShouldReplanTrip (autoPlan, isMobile, oldQuery, newQuery) {
140+
// Determine if either from/to location has changed
141+
const fromChanged = !isEqual(oldQuery.from, newQuery.from)
142+
const toChanged = !isEqual(oldQuery.to, newQuery.to)
143+
const oneLocationChanged = (fromChanged && !toChanged) || (!fromChanged && toChanged)
144+
// Check whether a trip should be auto-replanned
145+
const strategy = isMobile && autoPlan?.mobile
146+
? autoPlan?.mobile
147+
: autoPlan?.default
148+
const shouldReplanTrip = evaluateAutoPlanStrategy(
149+
strategy,
150+
fromChanged,
151+
toChanged,
152+
oneLocationChanged
153+
)
154+
return {
155+
fromChanged,
156+
oneLocationChanged,
157+
shouldReplanTrip,
158+
toChanged
159+
}
160+
}
161+
162+
/**
163+
* Shorthand method to evaluate auto plan strategy. It is assumed that this is
164+
* being called within the context of the `formChanged` action, so presumably
165+
* some query param has already changed. If further checking of query params is
166+
* needed, additional strategies should be added.
167+
*/
168+
const evaluateAutoPlanStrategy = (strategy, fromChanged, toChanged, oneLocationChanged) => {
169+
switch (strategy) {
170+
case 'ONE_LOCATION_CHANGED':
171+
if (oneLocationChanged) return true
172+
break
173+
case 'BOTH_LOCATIONS_CHANGED':
174+
if (fromChanged && toChanged) return true
175+
break
176+
case 'ANY': return true
177+
default: return false
178+
}
179+
}

Diff for: lib/reducers/create-otp-reducer.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ function validateInitialState (initialState) {
6666
*/
6767
export function getInitialState (userDefinedConfig) {
6868
const defaultConfig = {
69-
autoPlan: false,
69+
autoPlan: {
70+
mobile: 'BOTH_LOCATIONS_CHANGED',
71+
default: 'ONE_LOCATION_CHANGED'
72+
},
7073
debouncePlanTimeMs: 0,
7174
language: {},
7275
transitOperators: [],

0 commit comments

Comments
 (0)