+
{/* Sorted locations are shown regardless of tracking. */}
Date: Mon, 22 Feb 2021 17:27:27 -0500
Subject: [PATCH 020/270] refactor(StopViewer): Make tests pass
---
__tests__/test-utils/mock-data/store.js | 6 ++++--
lib/components/viewers/stop-viewer.js | 2 +-
lib/reducers/create-user-reducer.js | 23 ++++++++++++++++-------
3 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/__tests__/test-utils/mock-data/store.js b/__tests__/test-utils/mock-data/store.js
index 90e7a696e..b0e073caf 100644
--- a/__tests__/test-utils/mock-data/store.js
+++ b/__tests__/test-utils/mock-data/store.js
@@ -8,7 +8,8 @@ import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'
import thunk from 'redux-thunk'
-import {getInitialState} from '../../../lib/reducers/create-otp-reducer'
+import { getInitialState } from '../../../lib/reducers/create-otp-reducer'
+import { getUserInitialState } from '../../../lib/reducers/create-user-reducer'
Enzyme.configure({ adapter: new EnzymeReactAdapter() })
@@ -26,7 +27,8 @@ export function getMockInitialState () {
const mockInitialQuery = {}
return clone({
otp: getInitialState(mockConfig, mockInitialQuery),
- router: connectRouter(history)
+ router: connectRouter(history),
+ user: getUserInitialState(mockConfig)
})
}
diff --git a/lib/components/viewers/stop-viewer.js b/lib/components/viewers/stop-viewer.js
index e7c1bcd45..c6f9cfbba 100644
--- a/lib/components/viewers/stop-viewer.js
+++ b/lib/components/viewers/stop-viewer.js
@@ -356,7 +356,7 @@ const mapStateToProps = (state, ownProps) => {
const stopViewerConfig = getStopViewerConfig(state.otp)
return {
autoRefreshStopTimes: state.otp.user.autoRefreshStopTimes,
- favoriteStops: state.user.localUser.favoriteStops,
+ favoriteStops: (state.user.localUser && state.user.localUser.favoriteStops) || [],
homeTimezone: state.otp.config.homeTimezone,
viewedStop: state.otp.ui.viewedStop,
showUserSettings,
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index 8f1f13eff..9e8bb6aa0 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -21,8 +21,9 @@ const MAX_RECENT_STORAGE = 5
* are fetched separately as soon as user login info is received
* (from one of the components that uses withLoggedInUserSupport).
*/
-function loadUserFromLocalStoragePerConfig (config) {
- const { persistence = {} } = config
+function loadUserFromLocalStorage (config) {
+ const { locations: configLocations = null, persistence = {} } = config
+
const persistenceStrategy = persistence.enabled && persistence.strategy
if (persistenceStrategy === 'localStorage') {
// User's home and work locations
@@ -40,8 +41,8 @@ function loadUserFromLocalStoragePerConfig (config) {
// Filter valid locations found into locations list.
const locations = [home, work].filter(p => p)
// Add configurable locations to home and work locations
- if (config.locations) {
- locations.push(...config.locations.map(l => ({ ...l, type: 'suggested' })))
+ if (configLocations) {
+ locations.push(...configLocations.map(l => ({ ...l, type: 'suggested' })))
}
return {
@@ -62,10 +63,14 @@ function loadUserFromLocalStoragePerConfig (config) {
}
}
-function createUserReducer (config) {
- const localStorageState = loadUserFromLocalStoragePerConfig(config)
+/**
+ * Create the initial user state of otp-react-redux using the provided config, any
+ * and the user stored in localStorage.
+ */
+export function getUserInitialState (config) {
+ const localStorageState = loadUserFromLocalStorage(config)
- const initialState = {
+ return {
accessToken: null,
itineraryExistence: null,
lastPhoneSmsRequest: {
@@ -79,6 +84,10 @@ function createUserReducer (config) {
pathBeforeSignIn: null,
...localStorageState
}
+}
+
+function createUserReducer (config) {
+ const initialState = getUserInitialState(config)
return (state = initialState, action) => {
switch (action.type) {
From cb06ae592244905d4679e0781008ad6ade80bd29 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Mon, 22 Feb 2021 18:01:28 -0500
Subject: [PATCH 021/270] docs(create-user-reducer): Fix typo
---
lib/reducers/create-user-reducer.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index 9e8bb6aa0..d7dfbb4ce 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -17,7 +17,7 @@ const MAX_RECENT_STORAGE = 5
* - recent places in trip plan searches,
* - favorite stops
*
- * Note: If the persistence stragegy is otp_middleware, then user settings
+ * Note: If the persistence strategy is otp_middleware, then user settings
* are fetched separately as soon as user login info is received
* (from one of the components that uses withLoggedInUserSupport).
*/
From ca5e7107cfed74b17e848d92822d1ef916840cc5 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Tue, 23 Feb 2021 11:11:15 -0500
Subject: [PATCH 022/270] refactor(connect-location-field): Show places
according to persistence strategy.
---
lib/components/form/connect-location-field.js | 52 +++++++++++++------
lib/components/form/user-settings.js | 18 ++++---
lib/reducers/create-otp-reducer.js | 4 +-
lib/reducers/create-user-reducer.js | 9 ++--
lib/util/auth.js | 15 +++---
lib/util/constants.js | 3 +-
lib/util/user.js | 23 ++++----
7 files changed, 72 insertions(+), 52 deletions(-)
diff --git a/lib/components/form/connect-location-field.js b/lib/components/form/connect-location-field.js
index 1b3597322..dd257e9b1 100644
--- a/lib/components/form/connect-location-field.js
+++ b/lib/components/form/connect-location-field.js
@@ -3,9 +3,10 @@ import { connect } from 'react-redux'
import * as apiActions from '../../actions/api'
import * as locationActions from '../../actions/location'
import Icon from '../narrative/icon'
+import { PERSIST_TO_LOCAL_STORAGE, PERSIST_TO_OTP_MIDDLEWARE } from '../../util/constants'
import { getActiveSearch, getShowUserSettings } from '../../util/state'
import { isBlank } from '../../util/ui'
-import { convertToLocationFieldLocation } from '../../util/user'
+import { getPersistenceStrategy, isWork } from '../../util/user'
/**
* Custom icon component that renders based on the user location icon prop.
@@ -19,6 +20,19 @@ const UserLocationIcon = ({ userLocation }) => {
return
}
+/**
+ * Convert an entry from persisted user savedLocations into LocationField locations:
+ * - The icon for "Work" places is changed to 'work',
+ * - The name attribute is filled with the place address if available.
+ */
+function convertToLocationFieldLocation (place) {
+ return {
+ ...place,
+ icon: isWork(place) ? 'work' : place.icon,
+ name: place.address
+ }
+}
+
/**
* This higher-order component connects the target (styled) LocationField to the
* redux store.
@@ -43,21 +57,27 @@ export default function connectLocationField (StyledLocationField, options = {})
const { currentPosition, nearbyStops, sessionSearches } = location
const activeSearch = getActiveSearch(state.otp)
const query = activeSearch ? activeSearch.query : currentQuery
+ const { persistence } = config
+ const persistenceStrategy = getPersistenceStrategy(persistence)
- // Clone loggedInUserLocations with changes to conform to LocationField requirements:
- // - locations with blank addresses are removed.
- // - "Work" location icon name is changed to 'work',
- // - location.name is filled with the location address if available.
- const loggedInUserLocations = loggedInUser
- ? loggedInUser.savedLocations
- .filter(loc => !isBlank(loc.address))
- .map(convertToLocationFieldLocation)
- : []
-
- // Holds saved locations unless excluded in options.
- // see notes regarding persistence strategy
- // refactor obtaining the locations.
- const userSavedLocations = !excludeSavedLocations ? [...loggedInUserLocations, ...user.locations] : []
+ // Display saved locations and recent places according to the configured persistence strategy,
+ // unless displaying user locations is disabled via prop.
+ let userSavedLocations = []
+ let recentPlaces = []
+ if (!excludeSavedLocations) {
+ if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
+ // Remove locations with blank addresses, and
+ // modify loggedInUserLocations to conform to LocationField requirements.
+ userSavedLocations = loggedInUser
+ ? loggedInUser.savedLocations
+ .filter(loc => !isBlank(loc.address))
+ .map(convertToLocationFieldLocation)
+ : []
+ } else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
+ userSavedLocations = user.locations
+ recentPlaces = user.recentPlaces
+ }
+ }
const stateToProps = {
currentPosition,
@@ -67,7 +87,7 @@ export default function connectLocationField (StyledLocationField, options = {})
showUserSettings: getShowUserSettings(state.otp),
stopsIndex: transitIndex.stops,
UserLocationIconComponent: UserLocationIcon,
- userLocationsAndRecentPlaces: [...userSavedLocations, ...user.recentPlaces]
+ userLocationsAndRecentPlaces: [...userSavedLocations, ...recentPlaces]
}
// Set the location prop only if includeLocation is specified, else leave unset.
// Otherwise, the StyledLocationField component will use the fixed undefined/null value as location
diff --git a/lib/components/form/user-settings.js b/lib/components/form/user-settings.js
index cbc3d8fae..cd056370b 100644
--- a/lib/components/form/user-settings.js
+++ b/lib/components/form/user-settings.js
@@ -12,8 +12,12 @@ import * as userActions from '../../actions/user'
import { LinkWithQuery } from '../form/connected-links'
import MainPanelPlace from '../user/places/main-panel-place'
import PlaceShortcut from '../user/places/place-shortcut'
-import { PLACES_PATH } from '../../util/constants'
-import { isHome, isWork } from '../../util/user'
+import {
+ PERSIST_TO_LOCAL_STORAGE,
+ PERSIST_TO_OTP_MIDDLEWARE,
+ PLACES_PATH
+} from '../../util/constants'
+import { getPersistenceStrategy, isHome, isWork } from '../../util/user'
import { UnpaddedList } from './styled'
const { summarizeQuery } = coreUtils.query
@@ -77,7 +81,7 @@ class UserSettings extends Component {
trueUser
} = this.props
- if (loggedInUser && persistenceStrategy === 'otp_middleware') { // use constants
+ if (loggedInUser && persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
// Add id attribute using index, that will be used to call deleteUserPlace.
const loggedInUserLocations = loggedInUser.savedLocations.map((loc, index) => ({
...loc,
@@ -111,7 +115,7 @@ class UserSettings extends Component {
/>
)
- } else if (persistenceStrategy === 'localStorage') {
+ } else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
const { favoriteStops, storeTripHistory, recentPlaces } = localUser
// Clone locations in order to prevent blank locations from seeping into the
// app state/store.
@@ -284,9 +288,7 @@ const RecentTrips = ({ forgetSearch, setQueryParam, tripRequests = null, user })
const mapStateToProps = (state, ownProps) => {
const { config, currentQuery, location, transitIndex } = state.otp
- const { language, persistence = {} } = config
- console.log(config)
-
+ const { language, persistence } = config
const { localUser, localUserTripRequests, loggedInUser, loggedInUserTripRequests } = state.user
return {
config,
@@ -296,7 +298,7 @@ const mapStateToProps = (state, ownProps) => {
loggedInUser,
loggedInUserTripRequests,
nearbyStops: location.nearbyStops,
- persistenceStrategy: persistence.enabled && persistence.strategy,
+ persistenceStrategy: getPersistenceStrategy(persistence),
query: currentQuery,
sessionSearches: location.sessionSearches,
stopsIndex: transitIndex.stops,
diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js
index cc85a6989..feaf214a2 100644
--- a/lib/reducers/create-otp-reducer.js
+++ b/lib/reducers/create-otp-reducer.js
@@ -5,7 +5,7 @@ import objectPath from 'object-path'
import coreUtils from '@opentripplanner/core-utils'
import { MainPanelContent, MobileScreens } from '../actions/ui'
-import {FETCH_STATUS} from '../util/constants'
+import {FETCH_STATUS, PERSIST_TO_LOCAL_STORAGE} from '../util/constants'
import {getTimestamp} from '../util/state'
import {isBatchRoutingEnabled} from '../util/itinerary'
@@ -42,7 +42,7 @@ function validateInitialState (initialState) {
// See https://developers.arcgis.com/rest/geocode/api-reference/geocoding-free-vs-paid.htm
if (
objectPath.get(config, 'persistence.enabled') &&
- objectPath.get(config, 'persistence.strategy') === 'localStorage' &&
+ objectPath.get(config, 'persistence.strategy') === PERSIST_TO_LOCAL_STORAGE &&
objectPath.get(config, 'geocoder.type') === 'ARCGIS'
) {
errors.push(new Error('Local Storage persistence and ARCGIS geocoder cannot be enabled at the same time!'))
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index d7dfbb4ce..e967f126a 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -3,6 +3,9 @@ import clone from 'clone'
import update from 'immutability-helper'
import coreUtils from '@opentripplanner/core-utils'
+import { PERSIST_TO_LOCAL_STORAGE } from '../util/constants'
+import { getPersistenceStrategy } from '../util/user'
+
const { getItem, removeItem, storeItem } = coreUtils.storage
const MAX_RECENT_STORAGE = 5
@@ -22,10 +25,10 @@ const MAX_RECENT_STORAGE = 5
* (from one of the components that uses withLoggedInUserSupport).
*/
function loadUserFromLocalStorage (config) {
- const { locations: configLocations = null, persistence = {} } = config
+ const { locations: configLocations = null, persistence } = config
- const persistenceStrategy = persistence.enabled && persistence.strategy
- if (persistenceStrategy === 'localStorage') {
+ const persistenceStrategy = getPersistenceStrategy(persistence)
+ if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
// User's home and work locations
const home = getItem('home')
const work = getItem('work')
diff --git a/lib/util/auth.js b/lib/util/auth.js
index 1da8e5e08..3cf333482 100644
--- a/lib/util/auth.js
+++ b/lib/util/auth.js
@@ -1,4 +1,5 @@
-import { ACCOUNT_PATH, PERSISTENCE_STRATEGY_OTP_MIDDLEWARE } from './constants'
+import { ACCOUNT_PATH, PERSIST_TO_OTP_MIDDLEWARE } from './constants'
+import { getPersistenceStrategy } from './user'
/**
* Custom links under the user account dropdown.
@@ -20,17 +21,13 @@ export const accountLinks = [
/**
* Obtains the Auth0 {domain, audience, clientId} configuration, if the following applies in config.yml:
- * - persistence is defined,
- * - persistence.enabled is true,
- * - persistence.strategy is 'otp_middleware',
+ * - persistence.strategy is 'otp_middleware' (and persistence is enabled),
* - persistence.auth0 is defined.
* @param persistence The OTP persistence configuration from config.yml.
* @returns The Auth0 configuration, or null if the conditions are not met.
*/
export function getAuth0Config (persistence) {
- if (persistence) {
- const { enabled = false, strategy = null, auth0 = null } = persistence
- return (enabled && strategy === PERSISTENCE_STRATEGY_OTP_MIDDLEWARE) ? auth0 : null
- }
- return null
+ return getPersistenceStrategy(persistence) === PERSIST_TO_OTP_MIDDLEWARE
+ ? persistence.auth0
+ : null
}
diff --git a/lib/util/constants.js b/lib/util/constants.js
index 2d5f69964..c656fa5db 100644
--- a/lib/util/constants.js
+++ b/lib/util/constants.js
@@ -2,7 +2,8 @@ export const AUTH0_AUDIENCE = 'https://otp-middleware'
// This should match the value expected in otp-middleware OtpUser#AUTH0_SCOPE
export const AUTH0_SCOPE = 'otp-user'
export const DEFAULT_APP_TITLE = 'OpenTripPlanner'
-export const PERSISTENCE_STRATEGY_OTP_MIDDLEWARE = 'otp_middleware'
+export const PERSIST_TO_LOCAL_STORAGE = 'localStorage'
+export const PERSIST_TO_OTP_MIDDLEWARE = 'otp_middleware'
export const FETCH_STATUS = {
UNFETCHED: 0,
diff --git a/lib/util/user.js b/lib/util/user.js
index 25e1c2814..87efb6b80 100644
--- a/lib/util/user.js
+++ b/lib/util/user.js
@@ -85,19 +85,6 @@ export function positionHomeAndWorkFirst (userData) {
userData.savedLocations = reorderedLocations
}
-/**
- * Convert an entry from persisted user savedLocations into LocationField locations:
- * - The icon for "Work" places is changed to 'work',
- * - The name attribute is filled with the place address if available.
- */
-export function convertToLocationFieldLocation (place) {
- return {
- ...place,
- icon: isWork(place) ? 'work' : place.icon,
- name: place.address
- }
-}
-
/**
* Convert a LocationField entry to a persisted user savedLocations:
* - The icon for "Work" places is changed to 'briefcase',
@@ -113,3 +100,13 @@ export function convertToPlace (location) {
title: formatStoredPlaceName(location)
}
}
+
+/**
+ * Returns the persistence.strategy string if the following apply in config.yml, null otherwise:
+ * - persistence is defined,
+ * - persistence.enabled is true,
+ * - persistence.strategy is defined.
+ */
+export function getPersistenceStrategy (persistence) {
+ return persistence && persistence.enabled && persistence.strategy
+}
From 15a58dd1b1538139f1d874b36c7a30a22c73b6d7 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Tue, 23 Feb 2021 16:34:59 -0500
Subject: [PATCH 023/270] refactor(user reducer): Move TOGGLE_TRACKING action
to user reducer.
---
lib/reducers/create-otp-reducer.js | 17 -----------------
lib/reducers/create-user-reducer.js | 18 +++++++++++++++++-
2 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js
index feaf214a2..aa949a68b 100644
--- a/lib/reducers/create-otp-reducer.js
+++ b/lib/reducers/create-otp-reducer.js
@@ -505,23 +505,6 @@ function createOtpReducer (config, initialQuery) {
return update(state, { user: {
expandAdvanced: { $set: action.payload }
} })
- case 'TOGGLE_TRACKING': {
- storeItem('trackRecent', action.payload)
- let recentPlaces = clone(state.user.recentPlaces)
- let recentSearches = clone(state.user.recentSearches)
- if (!action.payload) {
- // If user disables tracking, remove recent searches and locations.
- recentPlaces = []
- recentSearches = []
- removeItem('recent')
- removeItem('recentSearches')
- }
- return update(state, { user: {
- trackRecent: { $set: action.payload },
- recentPlaces: { $set: recentPlaces },
- recentSearches: { $set: recentSearches }
- } })
- }
case 'REMEMBER_SEARCH':
const searches = clone(state.user.recentSearches)
const duplicateIndex = searches.findIndex(s => isEqual(s.query, action.payload.query))
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index e967f126a..5a38531c6 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -207,7 +207,6 @@ function createUserReducer (config) {
storeItem('favoriteStops', favoriteStops)
return update(state, { localUser: { favoriteStops: { $set: favoriteStops } } })
}
-
case 'FORGET_SEARCH': {
const recentSearches = clone(state.localUserTripRequests)
const index = recentSearches.findIndex(l => l.id === action.payload)
@@ -218,6 +217,23 @@ function createUserReducer (config) {
? update(state, { localUserTripRequests: { $splice: [[index, 1]] } })
: state
}
+ case 'TOGGLE_TRACKING': {
+ storeItem('trackRecent', action.payload)
+ let recentPlaces = clone(state.localUser.recentPlaces)
+ let recentSearches = clone(state.localUser.recentSearches)
+ if (!action.payload) {
+ // If user disables tracking, remove recent searches and locations.
+ recentPlaces = []
+ recentSearches = []
+ removeItem('recent')
+ removeItem('recentSearches')
+ }
+ return update(state, { localUser: {
+ recentPlaces: { $set: recentPlaces },
+ recentSearches: { $set: recentSearches },
+ storeTripHistory: { $set: action.payload }
+ } })
+ }
default:
return state
From bf8548cf8d80bc62c777c26ee6e0b14c908f8fa7 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Tue, 23 Feb 2021 16:53:28 -0500
Subject: [PATCH 024/270] refactor(user reducer): Move REMEMBER_SEARCH to user
reducer
---
lib/reducers/create-otp-reducer.js | 14 --------------
lib/reducers/create-user-reducer.js | 19 +++++++++++++++++++
2 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js
index aa949a68b..2f4311d98 100644
--- a/lib/reducers/create-otp-reducer.js
+++ b/lib/reducers/create-otp-reducer.js
@@ -1,6 +1,5 @@
import clone from 'clone'
import update from 'immutability-helper'
-import isEqual from 'lodash.isequal'
import objectPath from 'object-path'
import coreUtils from '@opentripplanner/core-utils'
@@ -505,19 +504,6 @@ function createOtpReducer (config, initialQuery) {
return update(state, { user: {
expandAdvanced: { $set: action.payload }
} })
- case 'REMEMBER_SEARCH':
- const searches = clone(state.user.recentSearches)
- const duplicateIndex = searches.findIndex(s => isEqual(s.query, action.payload.query))
- // Overwrite duplicate search (so that new timestamp is stored).
- if (duplicateIndex !== -1) searches[duplicateIndex] = action.payload
- else searches.unshift(action.payload)
- const sortedSearches = searches.sort((a, b) => b.timestamp - a.timestamp)
- // Ensure recent searches do not extend beyond MAX_RECENT_STORAGE
- if (sortedSearches.length >= MAX_RECENT_STORAGE) {
- sortedSearches.splice(MAX_RECENT_STORAGE)
- }
- storeItem('recentSearches', sortedSearches)
- return update(state, { user: { searches: { $set: sortedSearches } } })
case 'SET_AUTOPLAN':
return update(state, {
config: { autoPlan: { $set: action.payload.autoPlan } }
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index 5a38531c6..e8c4fdcf4 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -1,6 +1,7 @@
/* eslint-disable complexity */
import clone from 'clone'
import update from 'immutability-helper'
+import isEqual from 'lodash.isequal'
import coreUtils from '@opentripplanner/core-utils'
import { PERSIST_TO_LOCAL_STORAGE } from '../util/constants'
@@ -179,6 +180,7 @@ function createUserReducer (config) {
? update(state, { localUser: { favoriteStops: { $splice: [[removeIndex, 1]] } } })
: state
}
+
case 'REMEMBER_STOP': {
// Payload is stop data. We want to avoid saving other attributes that
// might be contained there (like lists of patterns).
@@ -207,6 +209,7 @@ function createUserReducer (config) {
storeItem('favoriteStops', favoriteStops)
return update(state, { localUser: { favoriteStops: { $set: favoriteStops } } })
}
+
case 'FORGET_SEARCH': {
const recentSearches = clone(state.localUserTripRequests)
const index = recentSearches.findIndex(l => l.id === action.payload)
@@ -217,6 +220,22 @@ function createUserReducer (config) {
? update(state, { localUserTripRequests: { $splice: [[index, 1]] } })
: state
}
+
+ case 'REMEMBER_SEARCH': {
+ const searches = clone(state.localUserTripRequests)
+ const duplicateIndex = searches.findIndex(s => isEqual(s.query, action.payload.query))
+ // Overwrite duplicate search (so that new timestamp is stored).
+ if (duplicateIndex !== -1) searches[duplicateIndex] = action.payload
+ else searches.unshift(action.payload)
+ const sortedSearches = searches.sort((a, b) => b.timestamp - a.timestamp)
+ // Ensure recent searches do not extend beyond MAX_RECENT_STORAGE
+ if (sortedSearches.length >= MAX_RECENT_STORAGE) {
+ sortedSearches.splice(MAX_RECENT_STORAGE)
+ }
+ storeItem('recentSearches', sortedSearches)
+ return update(state, { localUserTripRequests: { $set: sortedSearches } })
+ }
+
case 'TOGGLE_TRACKING': {
storeItem('trackRecent', action.payload)
let recentPlaces = clone(state.localUser.recentPlaces)
From 487466783fdf906d956929b9a76fdad4a934602b Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Tue, 23 Feb 2021 17:39:44 -0500
Subject: [PATCH 025/270] refactor(user reducer): Move FORGET_PLACE,
REMEMBER_PLACE to user reducer.
---
lib/actions/map.js | 43 ++++++++++-----
.../map/connected-endpoints-overlay.js | 9 ++--
lib/reducers/create-otp-reducer.js | 53 -------------------
lib/reducers/create-user-reducer.js | 31 +++++++++++
4 files changed, 66 insertions(+), 70 deletions(-)
diff --git a/lib/actions/map.js b/lib/actions/map.js
index 26cbcbcef..4ab38ceaa 100644
--- a/lib/actions/map.js
+++ b/lib/actions/map.js
@@ -19,12 +19,27 @@ import { clearActiveSearch } from './form'
// Private actions
const clearingLocation = createAction('CLEAR_LOCATION')
const settingLocation = createAction('SET_LOCATION')
+const deleteRecentPlace = createAction('DELETE_LOCAL_USER_RECENT_PLACE')
+const deleteSavedPlace = createAction('DELETE_LOCAL_USER_SAVED_PLACE')
// Public actions
-export const forgetPlace = createAction('FORGET_PLACE')
-export const rememberPlace = createAction('REMEMBER_PLACE')
-export const forgetStop = createAction('FORGET_STOP')
-export const rememberStop = createAction('REMEMBER_STOP')
+export const rememberPlace = createAction('REMEMBER_LOCAL_USER_PLACE')
+export const forgetStop = createAction('DELETE_LOCAL_USER_STOP')
+export const rememberStop = createAction('REMEMBER_LOCAL_USER_STOP')
+
+/**
+ * Dispatches the action to delete a saved or recent place from localStorage.
+ */
+export function forgetPlace (placeId) {
+ return function (dispatch, getState) {
+ // Recent place IDs contain the string literal 'recent'.
+ if (placeId.indexOf('recent') !== -1) {
+ dispatch(deleteRecentPlace(placeId))
+ } else {
+ dispatch(deleteSavedPlace(placeId))
+ }
+ }
+}
export function clearLocation (payload) {
return function (dispatch, getState) {
@@ -59,13 +74,13 @@ export function setLocation (payload) {
.reverse({ point: payload.location })
.then((location) => {
dispatch(settingLocation({
- locationType: payload.locationType,
- location
+ location,
+ locationType: payload.locationType
}))
}).catch(err => {
dispatch(settingLocation({
- locationType: payload.locationType,
- location: payload.location
+ location: payload.location,
+ locationType: payload.locationType
}))
console.warn(err)
})
@@ -83,10 +98,10 @@ export function setLocationToCurrent (payload) {
const currentPosition = getState().otp.location.currentPosition
if (currentPosition.error || !currentPosition.coords) return
payload.location = {
+ category: 'CURRENT_LOCATION',
lat: currentPosition.coords.latitude,
lon: currentPosition.coords.longitude,
- name: '(Current Location)',
- category: 'CURRENT_LOCATION'
+ name: '(Current Location)'
}
dispatch(settingLocation(payload))
}
@@ -97,12 +112,12 @@ export function switchLocations () {
const { from, to } = getState().otp.currentQuery
// First, reverse the locations.
dispatch(settingLocation({
- locationType: 'from',
- location: to
+ location: to,
+ locationType: 'from'
}))
dispatch(settingLocation({
- locationType: 'to',
- location: from
+ location: from,
+ locationType: 'to'
}))
// Then kick off a routing query (if the query is invalid, search will abort).
dispatch(routingQuery())
diff --git a/lib/components/map/connected-endpoints-overlay.js b/lib/components/map/connected-endpoints-overlay.js
index 0e85d3d80..ce4e7a465 100644
--- a/lib/components/map/connected-endpoints-overlay.js
+++ b/lib/components/map/connected-endpoints-overlay.js
@@ -21,10 +21,13 @@ const mapStateToProps = (state, ownProps) => {
// Intermediate places doesn't trigger a re-plan, so for now default to
// current query. FIXME: Determine with TriMet if this is desired behavior.
const places = state.otp.currentQuery.intermediatePlaces.filter(p => p)
+ const { localUser, loggedInUser } = state.user
+ const trueUser = loggedInUser || localUser
+
return {
fromLocation: from,
intermediatePlaces: places,
- locations: state.otp.user.locations,
+ locations: trueUser.savedLocations,
showUserSettings,
toLocation: to,
visible: true
@@ -32,10 +35,10 @@ const mapStateToProps = (state, ownProps) => {
}
const mapDispatchToProps = {
+ clearLocation,
forgetPlace,
rememberPlace,
- setLocation,
- clearLocation
+ setLocation
}
export default connect(mapStateToProps, mapDispatchToProps)(EndpointsOverlay)
diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js
index 2f4311d98..4a185db9b 100644
--- a/lib/reducers/create-otp-reducer.js
+++ b/lib/reducers/create-otp-reducer.js
@@ -9,7 +9,6 @@ import {getTimestamp} from '../util/state'
import {isBatchRoutingEnabled} from '../util/itinerary'
const { isTransit, getTransitModes } = coreUtils.itinerary
-const { matchLatLon } = coreUtils.map
const { filterProfileOptions } = coreUtils.profile
const {
ensureSingleAccessMode,
@@ -19,8 +18,6 @@ const {
const { getItem, removeItem, storeItem } = coreUtils.storage
const { getUserTimezone } = coreUtils.time
-const MAX_RECENT_STORAGE = 5
-
// TODO: fire planTrip action if default query is complete/error-free
/**
@@ -447,56 +444,6 @@ function createOtpReducer (config, initialQuery) {
case 'STORE_DEFAULT_SETTINGS':
storeItem('defaultQuery', action.payload)
return update(state, { user: { defaults: { $set: action.payload } } })
- case 'FORGET_PLACE': {
- // Payload is the place ID.
- // Recent place IDs contain the string literal 'recent'.
- if (action.payload.indexOf('recent') !== -1) {
- const recentPlaces = clone(state.user.recentPlaces)
- // Remove recent from list of recent places
- const removeIndex = recentPlaces.findIndex(l => l.id === action.payload)
- recentPlaces.splice(removeIndex, 1)
- storeItem('recent', recentPlaces)
- return removeIndex !== -1
- ? update(state, { user: { recentPlaces: { $splice: [[removeIndex, 1]] } } })
- : state
- } else {
- const locations = clone(state.user.locations)
- const removeIndex = locations.findIndex(l => l.id === action.payload)
- removeItem(action.payload)
- return removeIndex !== -1
- ? update(state, { user: { locations: { $splice: [[removeIndex, 1]] } } })
- : state
- }
- }
- case 'REMEMBER_PLACE': {
- const { location, type } = action.payload
- switch (type) {
- case 'recent': {
- const recentPlaces = clone(state.user.recentPlaces)
- const index = recentPlaces.findIndex(l => matchLatLon(l, location))
- // Replace recent place if duplicate found or add to list.
- if (index !== -1) recentPlaces.splice(index, 1, location)
- else recentPlaces.push(location)
- const sortedPlaces = recentPlaces.sort((a, b) => b.timestamp - a.timestamp)
- // Only keep up to 5 recent locations
- // FIXME: Check for duplicates
- if (recentPlaces.length >= MAX_RECENT_STORAGE) {
- sortedPlaces.splice(MAX_RECENT_STORAGE)
- }
- storeItem('recent', recentPlaces)
- return update(state, { user: { recentPlaces: { $set: sortedPlaces } } })
- }
- default: {
- const locations = clone(state.user.locations)
- // Determine if location type (e.g., home or work) already exists in list
- const index = locations.findIndex(l => l.type === type)
- if (index !== -1) locations.splice(index, 1, location)
- else locations.push(location)
- storeItem(type, location)
- return update(state, { user: { locations: { $set: locations } } })
- }
- }
- }
// FIXME: set up action
case 'TOGGLE_ADVANCED_OPTIONS':
storeItem('expandAdvanced', action.payload)
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index e8c4fdcf4..af4326595 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -7,6 +7,7 @@ import coreUtils from '@opentripplanner/core-utils'
import { PERSIST_TO_LOCAL_STORAGE } from '../util/constants'
import { getPersistenceStrategy } from '../util/user'
+const { matchLatLon } = coreUtils.map
const { getItem, removeItem, storeItem } = coreUtils.storage
const MAX_RECENT_STORAGE = 5
@@ -168,6 +169,36 @@ function createUserReducer (config) {
: state
}
+ case 'REMEMBER_LOCAL_USER_PLACE': {
+ const { location, type } = action.payload
+ switch (type) {
+ case 'recent': {
+ const recentPlaces = clone(state.localUser.recentPlaces)
+ const index = recentPlaces.findIndex(l => matchLatLon(l, location))
+ // Replace recent place if duplicate found or add to list.
+ if (index !== -1) recentPlaces.splice(index, 1, location)
+ else recentPlaces.push(location)
+ const sortedPlaces = recentPlaces.sort((a, b) => b.timestamp - a.timestamp)
+ // Only keep up to 5 recent locations
+ // FIXME: Check for duplicates
+ if (recentPlaces.length >= MAX_RECENT_STORAGE) {
+ sortedPlaces.splice(MAX_RECENT_STORAGE)
+ }
+ storeItem('recent', recentPlaces)
+ return update(state, { localUser: { recentPlaces: { $set: sortedPlaces } } })
+ }
+ default: {
+ const locations = clone(state.localUser.savedLocations)
+ // Determine if location type (e.g., home or work) already exists in list
+ const index = locations.findIndex(l => l.type === type)
+ if (index !== -1) locations.splice(index, 1, location)
+ else locations.push(location)
+ storeItem(type, location)
+ return update(state, { localUser: { savedLocations: { $set: locations } } })
+ }
+ }
+ }
+
case 'FORGET_STOP': {
const stopId = action.payload
const favoriteStops = clone(state.localUser.favoriteStops)
From 452627e90e28743b6f111ae1cecbeb6aef338827 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Tue, 23 Feb 2021 17:40:46 -0500
Subject: [PATCH 026/270] refactor(user reducer): Remove redundant prompt to
delete localStorage place
---
lib/reducers/create-user-reducer.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index af4326595..0bf36c01c 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -144,8 +144,6 @@ function createUserReducer (config) {
}
case 'DELETE_LOCAL_USER_RECENT_PLACE': {
- if (!confirm('Would you like to remove this place?')) return state
-
// This is used to delete the local user's recent location that matches the provided id.
const placeId = action.payload
const recentPlaces = clone(state.localUser.recentPlaces)
From 13d811d3ddb824925d61d3a8a8fd3f35e50379df Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Wed, 24 Feb 2021 12:12:56 -0500
Subject: [PATCH 027/270] refactor(user reducer): Refactor deletion from place
list.
---
lib/reducers/create-user-reducer.js | 53 +++++++++++++----------------
1 file changed, 24 insertions(+), 29 deletions(-)
diff --git a/lib/reducers/create-user-reducer.js b/lib/reducers/create-user-reducer.js
index 0bf36c01c..ccfd7de94 100644
--- a/lib/reducers/create-user-reducer.js
+++ b/lib/reducers/create-user-reducer.js
@@ -12,6 +12,23 @@ const { getItem, removeItem, storeItem } = coreUtils.storage
const MAX_RECENT_STORAGE = 5
+/**
+ * Removes a place by id from the specified localUser state and optional persistence setting.
+ */
+function removeLocalUserPlace (id, state, fieldName, settingName) {
+ const originalArray = state.localUser[fieldName]
+ const removeIndex = originalArray.findIndex(l => l.id === id)
+ // If a persistence setting is provided, create a new array without the specified element.
+ if (settingName) {
+ const newArray = clone(originalArray)
+ newArray.splice(removeIndex, 1)
+ storeItem(settingName, newArray)
+ }
+ return removeIndex !== -1
+ ? update(state, { localUser: { [fieldName]: { $splice: [[removeIndex, 1]] } } })
+ : state
+}
+
/**
* Load select user settings stored locally if the persistence strategy is localStorage.
* Other settings not mentioned below are still loaded through createOtpReducer.
@@ -101,6 +118,7 @@ function createUserReducer (config) {
accessToken: { $set: action.payload }
})
}
+
case 'SET_CURRENT_USER': {
return update(state, {
loggedInUser: { $set: action.payload }
@@ -143,28 +161,15 @@ function createUserReducer (config) {
})
}
- case 'DELETE_LOCAL_USER_RECENT_PLACE': {
+ case 'DELETE_LOCAL_USER_RECENT_PLACE':
// This is used to delete the local user's recent location that matches the provided id.
- const placeId = action.payload
- const recentPlaces = clone(state.localUser.recentPlaces)
- // Remove recent from list of recent places
- const removeIndex = recentPlaces.findIndex(l => l.id === placeId)
- recentPlaces.splice(removeIndex, 1)
- storeItem('recent', recentPlaces)
- return removeIndex !== -1
- ? update(state, { localUser: { recentPlaces: { $splice: [[removeIndex, 1]] } } })
- : state
- }
+ return removeLocalUserPlace(action.payload, state, 'recentPlaces', 'recent')
case 'DELETE_LOCAL_USER_SAVED_PLACE': {
// This is used to delete the local user's Home and Work (or other built-in)
// location that matches the provided id.
- const placeId = action.payload
- const removeIndex = state.localUser.savedLocations.findIndex(l => l.id === placeId)
- removeItem(placeId)
- return removeIndex !== -1
- ? update(state, { localUser: { savedLocations: { $splice: [[removeIndex, 1]] } } })
- : state
+ removeItem(action.payload)
+ return removeLocalUserPlace(action.payload, state, 'savedLocations')
}
case 'REMEMBER_LOCAL_USER_PLACE': {
@@ -197,18 +202,8 @@ function createUserReducer (config) {
}
}
- case 'FORGET_STOP': {
- const stopId = action.payload
- const favoriteStops = clone(state.localUser.favoriteStops)
-
- // This is used to delete the local user's favorite stop that matches the provided id.
- const removeIndex = favoriteStops.findIndex(l => l.id === stopId)
- favoriteStops.splice(removeIndex, 1)
- storeItem('favoriteStops', favoriteStops)
- return removeIndex !== -1
- ? update(state, { localUser: { favoriteStops: { $splice: [[removeIndex, 1]] } } })
- : state
- }
+ case 'FORGET_STOP':
+ return removeLocalUserPlace(action.payload, state, 'favoriteStops', 'favoriteStops')
case 'REMEMBER_STOP': {
// Payload is stop data. We want to avoid saving other attributes that
From f6acf45c7a9e08e7782a995635f4bdf7358d3615 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Wed, 24 Feb 2021 17:13:20 -0500
Subject: [PATCH 028/270] refactor: Make Save/Forget locations from map work
again.
---
lib/actions/api.js | 6 +-
lib/actions/map.js | 7 +-
lib/actions/user.js | 68 ++++++++++++++++++-
lib/components/form/connect-location-field.js | 38 +++--------
lib/components/form/user-settings.js | 8 +--
.../map/connected-endpoints-overlay.js | 16 ++++-
lib/components/user/places/favorite-place.js | 6 +-
lib/components/user/places/place-shortcut.js | 24 +++++--
lib/util/user.js | 44 ++++++++++--
9 files changed, 158 insertions(+), 59 deletions(-)
diff --git a/lib/actions/api.js b/lib/actions/api.js
index 5e901d58b..8951de93d 100644
--- a/lib/actions/api.js
+++ b/lib/actions/api.js
@@ -8,7 +8,7 @@ import queryParams from '@opentripplanner/core-utils/lib/query-params'
import { createAction } from 'redux-actions'
import qs from 'qs'
-import { rememberPlace } from './map'
+import { rememberPlace } from './user'
import { getStopViewerConfig, queryIsValid } from '../util/state'
import { getSecureFetchOptions } from '../util/middleware'
@@ -84,7 +84,7 @@ export function routingQuery (searchId = null) {
return async function (dispatch, getState) {
// FIXME: batchId is searchId for now.
const state = getState()
- const otpState = state.otp
+ const { otp: otpState, user: userState } = state
const isNewSearch = !searchId
if (isNewSearch) searchId = randId()
@@ -112,7 +112,7 @@ export function routingQuery (searchId = null) {
dispatch(routingResponse({ response: json, requestId, searchId }))
// If tracking is enabled, store locations and search after successful
// search is completed.
- if (otpState.user.trackRecent) {
+ if (userState.trueUser && userState.trueUser.storeTripHistory) {
const { from, to } = otpState.currentQuery
if (!isStoredPlace(from)) {
dispatch(rememberPlace({ type: 'recent', location: formatRecentPlace(from) }))
diff --git a/lib/actions/map.js b/lib/actions/map.js
index 4ab38ceaa..f2912acb4 100644
--- a/lib/actions/map.js
+++ b/lib/actions/map.js
@@ -4,6 +4,7 @@ import { createAction } from 'redux-actions'
import { routingQuery } from './api'
import { clearActiveSearch } from './form'
+import { deleteUserPlace } from './user'
/* SET_LOCATION action creator. Updates a from or to location in the store
*
@@ -20,10 +21,8 @@ import { clearActiveSearch } from './form'
const clearingLocation = createAction('CLEAR_LOCATION')
const settingLocation = createAction('SET_LOCATION')
const deleteRecentPlace = createAction('DELETE_LOCAL_USER_RECENT_PLACE')
-const deleteSavedPlace = createAction('DELETE_LOCAL_USER_SAVED_PLACE')
// Public actions
-export const rememberPlace = createAction('REMEMBER_LOCAL_USER_PLACE')
export const forgetStop = createAction('DELETE_LOCAL_USER_STOP')
export const rememberStop = createAction('REMEMBER_LOCAL_USER_STOP')
@@ -32,11 +31,11 @@ export const rememberStop = createAction('REMEMBER_LOCAL_USER_STOP')
*/
export function forgetPlace (placeId) {
return function (dispatch, getState) {
- // Recent place IDs contain the string literal 'recent'.
+ // localStorage only: Recent place IDs contain the string literal 'recent'.
if (placeId.indexOf('recent') !== -1) {
dispatch(deleteRecentPlace(placeId))
} else {
- dispatch(deleteSavedPlace(placeId))
+ dispatch(deleteUserPlace(placeId))
}
}
}
diff --git a/lib/actions/user.js b/lib/actions/user.js
index f31db421e..8324fef9b 100644
--- a/lib/actions/user.js
+++ b/lib/actions/user.js
@@ -7,10 +7,14 @@ import { createAction } from 'redux-actions'
import { routingQuery } from './api'
import { setQueryParam } from './form'
import { routeTo } from './ui'
-import { TRIPS_PATH } from '../util/constants'
+import {
+ PERSIST_TO_LOCAL_STORAGE,
+ PERSIST_TO_OTP_MIDDLEWARE,
+ TRIPS_PATH
+} from '../util/constants'
import { secureFetch } from '../util/middleware'
import { isBlank } from '../util/ui'
-import { isNewUser, positionHomeAndWorkFirst } from '../util/user'
+import { convertToPlace, getPersistenceStrategy, isHomeOrWork, isNewUser, positionHomeAndWorkFirst } from '../util/user'
const { planParamsToQuery } = coreUtils.query
const { OTP_API_DATE_FORMAT } = coreUtils.time
@@ -35,6 +39,7 @@ const setitineraryExistence = createAction('SET_ITINERARY_EXISTENCE')
export const deleteLocalUserRecentPlace = createAction('DELETE_LOCAL_USER_RECENT_PLACE')
export const deleteLocalUserSavedPlace = createAction('DELETE_LOCAL_USER_SAVED_PLACE')
export const deleteLocalUserStop = createAction('FORGET_STOP')
+const rememberLocalUserPlace = createAction('REMEMBER_LOCAL_USER_PLACE')
function createNewUser (auth0User) {
return {
@@ -501,7 +506,7 @@ export function saveUserPlace (placeToSave, placeIndex) {
/**
* Delete the place data at the specified index for the logged-in user.
*/
-export function deleteUserPlace (placeIndex) {
+export function deleteLoggedInUserPlace (placeIndex) {
return function (dispatch, getState) {
if (!confirm('Would you like to remove this place?')) return
@@ -511,3 +516,60 @@ export function deleteUserPlace (placeIndex) {
dispatch(createOrUpdateUser(loggedInUser, true))
}
}
+
+//Is there any way to do a factory interface??
+//If not, simplify the dispatch returns (remove dispatch).
+/**
+ * Delete place data by id for the logged-in or local user
+ * according to the persistence strategy.
+ */
+export function deleteUserPlace (placeId) {
+ return function (dispatch, getState) {
+ const { otp, user } = getState()
+ const persistenceStrategy = getPersistenceStrategy(otp.config.persistence)
+ const { loggedInUser } = user
+
+ if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE && loggedInUser) {
+ // For middleware loggedInUsers, this method should only be triggered by the
+ // 'Forget home' or 'Forget work' links from OTP UI's EndPointOverlay/EndPoint,
+ // with placeId set to 'home' or 'work'.
+ if (isHomeOrWork({ type: placeId })) {
+ // Find the index of the place in the loggedInUser.savedLocations
+ const placeIndex = loggedInUser.savedLocations.findIndex(loc => loc.type === placeId)
+ if (placeIndex > -1) {
+ dispatch(deleteLoggedInUserPlace(placeIndex))
+ }
+ }
+ } else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
+ dispatch(deleteLocalUserSavedPlace(placeId))
+ }
+ }
+}
+
+/**
+ * Remembers a place for the logged-in or local user
+ * according to the persistence strategy.
+ */
+export function rememberPlace (placeTypeLocation) {
+ return function (dispatch, getState) {
+ const { otp, user } = getState()
+ const persistenceStrategy = getPersistenceStrategy(otp.config.persistence)
+ const { loggedInUser } = user
+
+ if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE && loggedInUser) {
+ // For middleware loggedInUsers, this method should only be triggered by the
+ // 'Save as home' or 'Save as work' links from OTP UI's EndPointOverlay/EndPoint.
+ const { location } = placeTypeLocation
+ if (isHomeOrWork(location)) {
+ // Find the index of the place in the loggedInUser.savedLocations
+ const placeIndex = loggedInUser.savedLocations.findIndex(loc => loc.type === location.type)
+ if (placeIndex > -1) {
+ // Convert to loggedInUser saved place
+ dispatch(saveUserPlace(convertToPlace(location), placeIndex))
+ }
+ }
+ } else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
+ dispatch(rememberLocalUserPlace(placeTypeLocation))
+ }
+ }
+}
diff --git a/lib/components/form/connect-location-field.js b/lib/components/form/connect-location-field.js
index dd257e9b1..60f10a63a 100644
--- a/lib/components/form/connect-location-field.js
+++ b/lib/components/form/connect-location-field.js
@@ -5,8 +5,7 @@ import * as locationActions from '../../actions/location'
import Icon from '../narrative/icon'
import { PERSIST_TO_LOCAL_STORAGE, PERSIST_TO_OTP_MIDDLEWARE } from '../../util/constants'
import { getActiveSearch, getShowUserSettings } from '../../util/state'
-import { isBlank } from '../../util/ui'
-import { getPersistenceStrategy, isWork } from '../../util/user'
+import { getOtpUiLocations, getPersistenceStrategy } from '../../util/user'
/**
* Custom icon component that renders based on the user location icon prop.
@@ -20,19 +19,6 @@ const UserLocationIcon = ({ userLocation }) => {
return
}
-/**
- * Convert an entry from persisted user savedLocations into LocationField locations:
- * - The icon for "Work" places is changed to 'work',
- * - The name attribute is filled with the place address if available.
- */
-function convertToLocationFieldLocation (place) {
- return {
- ...place,
- icon: isWork(place) ? 'work' : place.icon,
- name: place.address
- }
-}
-
/**
* This higher-order component connects the target (styled) LocationField to the
* redux store.
@@ -52,30 +38,24 @@ export default function connectLocationField (StyledLocationField, options = {})
includeLocation = false
} = options
const mapStateToProps = (state, ownProps) => {
- const { config, currentQuery, location, transitIndex, user } = state.otp
- const { loggedInUser } = state.user
+ const { config, currentQuery, location, transitIndex } = state.otp
const { currentPosition, nearbyStops, sessionSearches } = location
const activeSearch = getActiveSearch(state.otp)
const query = activeSearch ? activeSearch.query : currentQuery
- const { persistence } = config
- const persistenceStrategy = getPersistenceStrategy(persistence)
// Display saved locations and recent places according to the configured persistence strategy,
- // unless displaying user locations is disabled via prop.
+ // unless displaying user locations is disabled via prop (e.g. in the saved-place editor
+ // when the loggedInUser defines their saved locations).
let userSavedLocations = []
let recentPlaces = []
if (!excludeSavedLocations) {
+ const { localUser, loggedInUser } = state.user
+ const persistenceStrategy = getPersistenceStrategy(config.persistence)
if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
- // Remove locations with blank addresses, and
- // modify loggedInUserLocations to conform to LocationField requirements.
- userSavedLocations = loggedInUser
- ? loggedInUser.savedLocations
- .filter(loc => !isBlank(loc.address))
- .map(convertToLocationFieldLocation)
- : []
+ userSavedLocations = getOtpUiLocations(loggedInUser)
} else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
- userSavedLocations = user.locations
- recentPlaces = user.recentPlaces
+ userSavedLocations = localUser.locations
+ recentPlaces = localUser.recentPlaces
}
}
diff --git a/lib/components/form/user-settings.js b/lib/components/form/user-settings.js
index cd056370b..9adf31725 100644
--- a/lib/components/form/user-settings.js
+++ b/lib/components/form/user-settings.js
@@ -69,7 +69,7 @@ class UserSettings extends Component {
deleteLocalUserRecentPlace,
deleteLocalUserSavedPlace,
deleteLocalUserStop,
- deleteUserPlace,
+ deleteLoggedInUserPlace,
forgetSearch,
localUser,
loggedInUser,
@@ -82,7 +82,7 @@ class UserSettings extends Component {
} = this.props
if (loggedInUser && persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
- // Add id attribute using index, that will be used to call deleteUserPlace.
+ // Add id attribute using index, that will be used to call deleteLoggedInUserPlace.
const loggedInUserLocations = loggedInUser.savedLocations.map((loc, index) => ({
...loc,
id: index
@@ -102,7 +102,7 @@ class UserSettings extends Component {
@@ -312,7 +312,7 @@ const mapDispatchToProps = {
deleteLocalUserRecentPlace: userActions.deleteLocalUserRecentPlace,
deleteLocalUserSavedPlace: userActions.deleteLocalUserSavedPlace,
deleteLocalUserStop: userActions.deleteLocalUserStop,
- deleteUserPlace: userActions.deleteUserPlace,
+ deleteLoggedInUserPlace: userActions.deleteLoggedInUserPlace,
forgetSearch: apiActions.forgetSearch,
setLocation: mapActions.setLocation,
setQueryParam: formActions.setQueryParam,
diff --git a/lib/components/map/connected-endpoints-overlay.js b/lib/components/map/connected-endpoints-overlay.js
index ce4e7a465..267b371d2 100644
--- a/lib/components/map/connected-endpoints-overlay.js
+++ b/lib/components/map/connected-endpoints-overlay.js
@@ -4,10 +4,12 @@ import { connect } from 'react-redux'
import {
clearLocation,
forgetPlace,
- rememberPlace,
setLocation
} from '../../actions/map'
+import { rememberPlace } from '../../actions/user'
+import { PERSIST_TO_LOCAL_STORAGE, PERSIST_TO_OTP_MIDDLEWARE } from '../../util/constants'
import { getActiveSearch, getShowUserSettings } from '../../util/state'
+import { getOtpUiLocations, getPersistenceStrategy } from '../../util/user'
// connect to the redux store
@@ -22,12 +24,20 @@ const mapStateToProps = (state, ownProps) => {
// current query. FIXME: Determine with TriMet if this is desired behavior.
const places = state.otp.currentQuery.intermediatePlaces.filter(p => p)
const { localUser, loggedInUser } = state.user
- const trueUser = loggedInUser || localUser
+
+ const { persistence } = state.otp.config
+ const persistenceStrategy = getPersistenceStrategy(persistence)
+ let userSavedLocations = []
+ if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
+ userSavedLocations = getOtpUiLocations(loggedInUser)
+ } else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
+ userSavedLocations = localUser.locations
+ }
return {
fromLocation: from,
intermediatePlaces: places,
- locations: trueUser.savedLocations,
+ locations: userSavedLocations,
showUserSettings,
toLocation: to,
visible: true
diff --git a/lib/components/user/places/favorite-place.js b/lib/components/user/places/favorite-place.js
index b8e587d68..c47edf0da 100644
--- a/lib/components/user/places/favorite-place.js
+++ b/lib/components/user/places/favorite-place.js
@@ -54,8 +54,8 @@ const StyledPlace = styled(Place)`
*/
class FavoritePlace extends Component {
_handleDelete = () => {
- const { deleteUserPlace, place } = this.props
- deleteUserPlace(place.id)
+ const { deleteLoggedInUserPlace, place } = this.props
+ deleteLoggedInUserPlace(place.id)
}
render () {
@@ -82,7 +82,7 @@ const mapStateToProps = (state, ownProps) => {
}
const mapDispatchToProps = {
- deleteUserPlace: userActions.deleteUserPlace
+ deleteLoggedInUserPlace: userActions.deleteLoggedInUserPlace
}
export default connect(mapStateToProps, mapDispatchToProps)(FavoritePlace)
diff --git a/lib/components/user/places/place-shortcut.js b/lib/components/user/places/place-shortcut.js
index 452a4bb46..d9e289244 100644
--- a/lib/components/user/places/place-shortcut.js
+++ b/lib/components/user/places/place-shortcut.js
@@ -3,10 +3,23 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as mapActions from '../../../actions/map'
-import { convertToPlace } from '../../../util/user'
+import { convertToLocation, convertToPlace } from '../../../util/user'
import MainPanelPlace from './main-panel-place'
-const { matchLatLon } = coreUtils.map
+const { formatStoredPlaceName, getDetailText, matchLatLon } = coreUtils.map
+
+/**
+ * Calls convertToPlace, and adds some more fields to the resulting place for rendering.
+ */
+function convertToPlaceWithDetails (location) {
+ const place = convertToPlace(location)
+ return {
+ ...place,
+ details: getDetailText(location),
+ id: location.id,
+ title: formatStoredPlaceName(location)
+ }
+}
/**
* A shortcut button that sets the provided place as the 'from' or 'to' Place.
@@ -39,7 +52,10 @@ class PlaceShortcut extends Component {
if (place.blank) {
window.alert(`Enter origin/destination in the form (or set via map click) and click the resulting marker to set as ${place.type} location.`)
} else {
- this._setFromOrToLocation(place)
+ // Convert to OTP UI location before sending events
+ // (to avoid issues when user clicks Forget/Save location
+ // multiple times subsequently)
+ this._setFromOrToLocation(convertToLocation(place))
}
}
@@ -57,7 +73,7 @@ class PlaceShortcut extends Component {
const { onDelete, onView, path, place } = this.props
// localStorage places (where path is not provided) need to be converted,
// so the correct fields are passed to MainPanelPlace.
- const convertedPlace = path ? place : convertToPlace(place)
+ const convertedPlace = path ? place : convertToPlaceWithDetails(place)
return (
!isBlank(place.address))
+ .map(convertToLocation)
+ : []
+}
From 7d7604b48165d4cf6ef4565595b96358a0ce21f4 Mon Sep 17 00:00:00 2001
From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com>
Date: Thu, 25 Feb 2021 19:16:33 -0500
Subject: [PATCH 029/270] refactor(user/places/place): Remove actions (use
explicit handlers)
---
lib/components/form/connect-location-field.js | 2 +-
lib/components/form/user-settings.js | 38 ++---
.../map/connected-endpoints-overlay.js | 2 +-
.../user/places/favorite-place-list.js | 10 +-
lib/components/user/places/favorite-place.js | 15 +-
.../user/places/main-panel-place.js | 21 ++-
lib/components/user/places/place-shortcut.js | 32 +++-
lib/components/user/places/place.js | 159 +++++++-----------
lib/util/user.js | 12 +-
9 files changed, 141 insertions(+), 150 deletions(-)
diff --git a/lib/components/form/connect-location-field.js b/lib/components/form/connect-location-field.js
index 60f10a63a..d2069ec21 100644
--- a/lib/components/form/connect-location-field.js
+++ b/lib/components/form/connect-location-field.js
@@ -54,7 +54,7 @@ export default function connectLocationField (StyledLocationField, options = {})
if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
userSavedLocations = getOtpUiLocations(loggedInUser)
} else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
- userSavedLocations = localUser.locations
+ userSavedLocations = localUser.savedLocations
recentPlaces = localUser.recentPlaces
}
}
diff --git a/lib/components/form/user-settings.js b/lib/components/form/user-settings.js
index 9adf31725..797ec36ec 100644
--- a/lib/components/form/user-settings.js
+++ b/lib/components/form/user-settings.js
@@ -17,7 +17,8 @@ import {
PERSIST_TO_OTP_MIDDLEWARE,
PLACES_PATH
} from '../../util/constants'
-import { getPersistenceStrategy, isHome, isWork } from '../../util/user'
+import { isBlank } from '../../util/ui'
+import { canDeletePlace, getPersistenceStrategy, isHome, isWork } from '../../util/user'
import { UnpaddedList } from './styled'
const { summarizeQuery } = coreUtils.query
@@ -85,6 +86,7 @@ class UserSettings extends Component {
// Add id attribute using index, that will be used to call deleteLoggedInUserPlace.
const loggedInUserLocations = loggedInUser.savedLocations.map((loc, index) => ({
...loc,
+ blank: isBlank(loc.address),
id: index
}))
const savedPlacesHeader = (
@@ -204,16 +206,18 @@ const Places = ({
{header && {header}
}
{places.length > 0
- ? places.map((location, index) => (
-
- ))
- : textIfEmpty && {textIfEmpty}
+ ? places.map((location, index) => {
+ return (
+
+ )
+ })
+ : (textIfEmpty && {textIfEmpty})
}
>
@@ -239,18 +243,14 @@ class TripRequest extends Component {
const name = summarizeQuery(query, user.savedLocations)
const timeInfo = moment(timestamp).fromNow()
- const place = {
- details: timeInfo,
- icon: 'clock-o',
- name,
- title: `${name} (${timeInfo})`
- }
-
return (
)
}
diff --git a/lib/components/map/connected-endpoints-overlay.js b/lib/components/map/connected-endpoints-overlay.js
index 267b371d2..4bcf2a2c3 100644
--- a/lib/components/map/connected-endpoints-overlay.js
+++ b/lib/components/map/connected-endpoints-overlay.js
@@ -31,7 +31,7 @@ const mapStateToProps = (state, ownProps) => {
if (persistenceStrategy === PERSIST_TO_OTP_MIDDLEWARE) {
userSavedLocations = getOtpUiLocations(loggedInUser)
} else if (persistenceStrategy === PERSIST_TO_LOCAL_STORAGE) {
- userSavedLocations = localUser.locations
+ userSavedLocations = localUser.savedLocations
}
return {
diff --git a/lib/components/user/places/favorite-place-list.js b/lib/components/user/places/favorite-place-list.js
index 1099ee06e..f48c32435 100644
--- a/lib/components/user/places/favorite-place-list.js
+++ b/lib/components/user/places/favorite-place-list.js
@@ -4,7 +4,7 @@ import { connect } from 'react-redux'
import { UnpaddedList } from '../../form/styled'
import { CREATE_ACCOUNT_PLACES_PATH, PLACES_PATH } from '../../../util/constants'
-import FavoritePlace from './favorite-place'
+import FavoritePlace, { StyledPlace } from './favorite-place'
/**
* Renders an editable list user's favorite locations, and lets the user add a new one.
@@ -18,6 +18,7 @@ const FavoritePlaceList = ({ isCreating, loggedInUser }) => {
{savedLocations.map((place, index) => (
{
)}
{/* For adding a new place. */}
-
+
)
diff --git a/lib/components/user/places/favorite-place.js b/lib/components/user/places/favorite-place.js
index c47edf0da..f4e7dab8d 100644
--- a/lib/components/user/places/favorite-place.js
+++ b/lib/components/user/places/favorite-place.js
@@ -3,11 +3,11 @@ import { connect } from 'react-redux'
import styled from 'styled-components'
import * as userActions from '../../../actions/user'
+import { canDeletePlace } from '../../../util/user'
import Place, {
ActionButton,
ActionButtonPlaceholder,
EDIT_LABEL,
- getActionsForPlace,
PlaceButton,
PlaceContent,
PlaceDetail,
@@ -16,7 +16,7 @@ import Place, {
const FIELD_HEIGHT_PX = '60px'
-const StyledPlace = styled(Place)`
+export const StyledPlace = styled(Place)`
align-items: stretch;
display: flex;
height: ${FIELD_HEIGHT_PX};
@@ -54,21 +54,24 @@ const StyledPlace = styled(Place)`
*/
class FavoritePlace extends Component {
_handleDelete = () => {
- const { deleteLoggedInUserPlace, place } = this.props
- deleteLoggedInUserPlace(place.id)
+ const { deleteLoggedInUserPlace, index } = this.props
+ deleteLoggedInUserPlace(index)
}
render () {
const { path, place } = this.props
const label = place && EDIT_LABEL
+ const { address, icon, name, type } = place
return (