-
Notifications
You must be signed in to change notification settings - Fork 0
ERA-11239: track styles: patrol tracks (2) #1251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
flex-wrap: wrap; | ||
height: var(--legend-height); | ||
line-height: normal; | ||
margin-bottom: 0.5rem; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
padding: 0.5rem; | ||
position: relative; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'; | |
import { caseInsensitiveCompare } from '../utils/string'; | ||
import { INITIAL_FILTER_STATE, updatePatrolFilter } from '../ducks/patrol-filter'; | ||
import { resetGlobalDateRange } from '../ducks/global-date-range'; | ||
import { selectPatrolsFeedMappedFromStore } from '../selectors/patrols'; | ||
import { isFilterModified } from '../utils/patrol-filter'; | ||
import { trackEventFactory, PATROL_FILTER_CATEGORY } from '../utils/analytics'; | ||
|
||
|
@@ -33,7 +34,7 @@ const PatrolFilter = ({ className }) => { | |
const containerRef = useRef(null); | ||
const { t } = useTranslation('filters', { keyPrefix: 'patrolFilters' }); | ||
const dispatch = useDispatch(); | ||
const patrolStore = useSelector((state) => state.data.patrolStore); | ||
const patrolsFeedMappedFromStore = useSelector(selectPatrolsFeedMappedFromStore); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
const patrolFilter = useSelector(state => state.data.patrolFilter); | ||
|
||
const [filterText, setFilterText] = useState(patrolFilter.filter.text); | ||
|
@@ -159,7 +160,7 @@ const PatrolFilter = ({ className }) => { | |
className={styles.friendlyFilterString} | ||
dateRange={patrolFilter.filter.date_range} | ||
isFiltered={isFilterModified(patrolFilter)} | ||
totalFeedCount={Object.keys(patrolStore).length ?? 0} | ||
totalFeedCount={patrolsFeedMappedFromStore.length} | ||
/> | ||
|
||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,15 +36,18 @@ const PatrolTrackLegend = () => { | |
// Build the items array with the description, icon, id and title of each tracked patrol. | ||
const items = useMemo(() => patrolsWithTrackData.map((patrolData) => { | ||
const iconId = iconTypeForPatrol(patrolData.patrol); | ||
const title = displayTitleForPatrol(patrolData.patrol, patrolData.leader); | ||
const patrolTitle = displayTitleForPatrol(patrolData.patrol, patrolData.leader); | ||
|
||
return { | ||
description: t('itemDescription', { | ||
length: `${patrolData.trackData ? length(patrolData.trackData.track).toFixed(2): 0.00}km`, | ||
}), | ||
icon: <DasIcon className={styles.itemIcon} iconId={iconId} title={t('icon', { title })} type="events" />, | ||
description: `${patrolData.trackData ? length(patrolData.trackData.track).toFixed(2): 0.00}km`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the new translation for the item title and remove the unnecessary translation for the description, leave just the distance. |
||
icon: <DasIcon | ||
className={styles.itemIcon} | ||
iconId={iconId} | ||
title={t('icon', { patrolTitle })} | ||
type="events" | ||
/>, | ||
id: patrolData.patrol.id, | ||
title, | ||
title: t('itemTitle', { patrolTitle }), | ||
}; | ||
}), [patrolsWithTrackData, t]); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,24 @@ | ||
import React, { memo, useCallback, useMemo } from 'react'; | ||
import React, { memo, useMemo } from 'react'; | ||
import { useSelector } from 'react-redux'; | ||
|
||
import useNavigate from '../../hooks/useNavigate'; | ||
import { selectPatrolsFeedMappedFromStore } from '../../selectors/patrols'; | ||
import { sortPatrolList } from '../../utils/patrols'; | ||
|
||
import PatrolFilter from '../../PatrolFilter'; | ||
import PatrolList from '../../PatrolList'; | ||
import { sortPatrolList } from '../../utils/patrols'; | ||
|
||
const PatrolsFeedTab = ({ loadingPatrolsFeed }) => { | ||
const navigate= useNavigate(); | ||
const patrolStore = useSelector((state) => state.data.patrolStore); | ||
const sortedPatrols = useMemo(() => sortPatrolList(Object.values(patrolStore)), [patrolStore]); | ||
|
||
const onItemClick = useCallback((id) => navigate(id), [navigate]); | ||
const patrolsFeedMappedFromStore = useSelector(selectPatrolsFeedMappedFromStore); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
const sortedPatrols = useMemo(() => sortPatrolList(patrolsFeedMappedFromStore), [patrolsFeedMappedFromStore]); | ||
|
||
return <> | ||
<PatrolFilter /> | ||
<PatrolList loading={loadingPatrolsFeed} onItemClick={onItemClick} patrols={sortedPatrols} /> | ||
|
||
<PatrolList loading={loadingPatrolsFeed} onItemClick={(id) => navigate(id)} patrols={sortedPatrols} /> | ||
</>; | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | ||
import { useEffect, useMemo, useRef, useState } from 'react'; | ||
import cloneDeep from 'lodash/cloneDeep'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
|
||
import { fetchPatrols } from '../../ducks/patrols'; | ||
import { fetchPatrolsFeed } from '../../ducks/patrols'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only logical change in this file is rename the old |
||
|
||
const useFetchPatrolsFeed = () => { | ||
const dispatch = useDispatch(); | ||
|
@@ -20,18 +20,15 @@ const useFetchPatrolsFeed = () => { | |
return filterParams; | ||
}, [patrolFilter]); | ||
|
||
const fetchAndLoadPatrolData = useCallback(() => { | ||
patrolFetchRef.current = dispatch(fetchPatrols()); | ||
useEffect(() => { | ||
setLoadingPatrolsFeed(true); | ||
|
||
patrolFetchRef.current = dispatch(fetchPatrolsFeed()); | ||
|
||
patrolFetchRef.current.request.finally(() => { | ||
setLoadingPatrolsFeed(false); | ||
patrolFetchRef.current = null; | ||
}); | ||
}, [dispatch]); | ||
|
||
useEffect(() => { | ||
setLoadingPatrolsFeed(true); | ||
fetchAndLoadPatrolData(); | ||
|
||
return () => { | ||
const priorRequestCancelToken = patrolFetchRef?.current?.cancelToken; | ||
|
@@ -40,7 +37,7 @@ const useFetchPatrolsFeed = () => { | |
priorRequestCancelToken.cancel(); | ||
} | ||
}; | ||
}, [fetchAndLoadPatrolData, patrolFilterParams]); | ||
}, [dispatch, patrolFilterParams]); | ||
|
||
return { loadingPatrolsFeed }; | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,16 @@ | ||
import axios, { CancelToken, isCancel } from 'axios'; | ||
import axios, { CancelToken } from 'axios'; | ||
import merge from 'lodash/merge'; | ||
|
||
import { API_URL } from '../constants'; | ||
|
||
import { calcPatrolFilterForRequest/* , | ||
validatePatrolAgainstCurrentPatrolFilter */ } from '../utils/patrol-filter'; | ||
import { calcPatrolFilterForRequest } from '../utils/patrol-filter'; | ||
import globallyResettableReducer from '../reducers/global-resettable'; | ||
|
||
export const PATROLS_API_URL = `${API_URL}activity/patrols/`; | ||
|
||
const FETCH_PATROLS_SUCCESS = 'FETCH_PATROLS_SUCCESS'; | ||
const UPDATE_PATROL_STORE = 'UPDATE_PATROL_STORE'; | ||
const FETCH_PATROLS_ERROR = 'FETCH_PATROLS_ERROR'; | ||
|
||
const CREATE_PATROL_SUCCESS = 'CREATE_PATROL_SUCCESS'; | ||
// const CREATE_PATROL_ERROR = 'CREATE_PATROL_ERROR'; | ||
|
||
const UPDATE_PATROL_ERROR = 'UPDATE_PATROL_ERROR'; | ||
|
||
|
@@ -23,7 +20,6 @@ const UPLOAD_PATROL_FILES_START = 'UPLOAD_PATROL_FILES_START'; | |
const UPLOAD_PATROL_FILES_SUCCESS = 'UPLOAD_PATROL_FILES_SUCCESS'; | ||
const UPLOAD_PATROL_FILES_ERROR = 'UPLOAD_PATROL_FILES_ERROR'; | ||
|
||
|
||
const CLEAR_PATROL_DATA = 'CLEAR_PATROL_DATA'; | ||
|
||
const REMOVE_PATROL_BY_ID = 'REMOVE_PATROL_BY_ID'; | ||
|
@@ -74,14 +70,6 @@ export const updatePatrolStore = (patrols) => ({ | |
payload: patrols, | ||
}); | ||
|
||
export const fetchPatrolsSuccess = (patrols) => (dispatch) => { | ||
dispatch({ | ||
type: FETCH_PATROLS_SUCCESS, | ||
payload: patrols, | ||
}); | ||
dispatch(updatePatrolStore(patrols)); | ||
}; | ||
|
||
export const socketDeletePatrol = (payload) => (dispatch) => { | ||
const { patrol_id, matches_current_filter } = payload; | ||
if (matches_current_filter) { | ||
|
@@ -106,33 +94,6 @@ export const fetchPatrol = id => dispatch => axios.get(`${PATROLS_API_URL}${id}` | |
throw error; | ||
}); | ||
|
||
export const fetchPatrols = () => (dispatch) => { | ||
let cancelToken = CancelToken.source(); | ||
|
||
const patrolFilterParamString = calcPatrolFilterForRequest({ params: { page_size: 200 } }); | ||
|
||
const request = axios.get(`${PATROLS_API_URL}?${patrolFilterParamString}`, { | ||
cancelToken: cancelToken.token, | ||
}) | ||
.then(({ data: { data: patrols } }) => { | ||
dispatch(fetchPatrolsSuccess(patrols)); | ||
return patrols; | ||
|
||
}) | ||
.catch((error) => { | ||
console.warn('error fetching patrols', error); | ||
dispatch({ | ||
type: FETCH_PATROLS_ERROR, | ||
payload: error, | ||
}); | ||
if (!isCancel(error)) { | ||
return new Error(error); | ||
} | ||
}); | ||
|
||
return { request, cancelToken }; | ||
}; | ||
|
||
export const createPatrol = (patrol) => (dispatch) => { | ||
|
||
return axios.post(PATROLS_API_URL, patrol) | ||
|
@@ -313,3 +274,49 @@ export const patrolTracksReducer = (state = INITIAL_PATROL_TRACKS_STATE, { type, | |
|
||
return state; | ||
}; | ||
|
||
// Actions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename and refactor the |
||
const FETCH_PATROLS_FEED_SUCCESS = 'FETCH_PATROLS_FEED_SUCCESS'; | ||
|
||
// Action creators | ||
export const fetchPatrolsFeed = () => (dispatch) => { | ||
const cancelToken = CancelToken.source(); | ||
|
||
const request = axios.get( | ||
`${PATROLS_API_URL}?${calcPatrolFilterForRequest({ params: { page_size: 200 } })}`, | ||
{ cancelToken: cancelToken.token } | ||
) | ||
.then((response) => { | ||
dispatch({ payload: response.data.data.results.map((patrol) => patrol.id), type: FETCH_PATROLS_FEED_SUCCESS }); | ||
dispatch(updatePatrolStore(response.data.data)); | ||
}) | ||
.catch((error) => { | ||
dispatch({ payload: error, type: FETCH_PATROLS_ERROR }); | ||
|
||
console.warn('error fetching patrols', error); | ||
}); | ||
|
||
return { cancelToken, request }; | ||
}; | ||
|
||
// Reducer | ||
export const INITIAL_PATROLS_FEED_STATE = []; | ||
|
||
export const patrolsFeedReducer = globallyResettableReducer((state, action) => { | ||
switch (action.type) { | ||
case FETCH_PATROLS_FEED_SUCCESS: | ||
return action.payload; | ||
|
||
case CREATE_PATROL_REALTIME: | ||
if (!state.includes(action.payload.id)) { | ||
return [action.payload.id, ...state]; | ||
} | ||
return state; | ||
|
||
case REMOVE_PATROL_BY_ID: | ||
return state.filter((patrolId) => patrolId !== action.payload); | ||
|
||
default: | ||
return state; | ||
} | ||
}, INITIAL_PATROLS_FEED_STATE); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ import eventStoreReducer, { mapEventsReducer, eventFeedReducer, incidentFeedRedu | |
import eventCategoriesReducer from '../ducks/event-categories'; | ||
import eventTypesReducer from '../ducks/event-types'; | ||
import observationsReducer from '../ducks/observations'; | ||
import { patrolStoreReducer, patrolTracksReducer } from '../ducks/patrols'; | ||
import { patrolsFeedReducer, patrolStoreReducer, patrolTracksReducer } from '../ducks/patrols'; | ||
import patrolTypesReducer from '../ducks/patrol-types'; | ||
import patrolFilterReducer, { persistenceConfig as patrolFilterPersistenceConfig } from '../ducks/patrol-filter'; | ||
import mapsReducer, { homeMapReducer } from '../ducks/maps'; | ||
|
@@ -68,6 +68,7 @@ const rootReducer = combineReducers({ | |
data: combineReducers({ | ||
baseLayers: baseLayersReducer, | ||
eventStore: eventStoreReducer, | ||
patrolsFeed: patrolsFeedReducer, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add back the clean version of the |
||
patrolStore: patrolStoreReducer, | ||
feedEvents: eventFeedReducer, | ||
feedIncidents: incidentFeedReducer, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,6 +66,7 @@ const buildPatrolData = (patrol, timeSliderState, tracks) => { | |
return patrolData; | ||
}; | ||
|
||
const selectPatrolsFeed = (state) => state.data.patrolsFeed; | ||
const selectPatrolLeaderSchema = (state) => state.data.patrolLeaderSchema; | ||
const selectPatrolStore = (state) => state.data.patrolStore; | ||
const selectPatrolTrackState = (state) => state.view.patrolTrackState; | ||
|
@@ -111,6 +112,12 @@ export const selectPatrolLeadersWithLastPosition = createSelector( | |
}) : null | ||
); | ||
|
||
export const selectPatrolsFeedMappedFromStore = createSelector( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new selector that maps the patrols feed stored in the new reducer to their patrol objects in the patrol store. |
||
[selectPatrolsFeed, selectPatrolStore], | ||
// Map each patrol id from the feed to its patrol object and filter just the defined ones. | ||
(patrolsFeed, patrolStore) => patrolsFeed.map((patrolId) => patrolStore[patrolId]).filter((patrol) => !!patrol), | ||
); | ||
|
||
export const selectPatrolsWithTracks = createSelector( | ||
[selectPatrolStore, selectVisibleAndPinnedPatrolTracks], | ||
(patrolStore, visibleAndPinnedPatrolTracks) => visibleAndPinnedPatrolTracks | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two updates in the translations file: