Skip to content

ERA-11239: track styles: patrol tracks #1244

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

Merged
merged 5 commits into from
Mar 4, 2025
Merged

ERA-11239: track styles: patrol tracks #1244

merged 5 commits into from
Mar 4, 2025

Conversation

luixlive
Copy link
Contributor

@luixlive luixlive commented Feb 28, 2025

What does this PR do?

Re-do the PatrolTrackLegend component so it has the look and feel like the new SubjectTrackLegend.

How does it look

Desktop:
image

Mobile:
image

Relevant link(s)

Where / how to start reviewing (optional)

1- Start by reviewing the new component src/TrackLegend/index.js. It's code is around 80% of what SubjectTrackLegend used to be. I just removed the subject internal details so new abstraction is not tied to subjects, and we can also use it to render patrol tracks.
2- After understanding the code of TrackLegend, reviewing PatrolTrackLegend and SubjectTrackLegend will be trivial.
3- The "last" important thing to review is the refactoring and optimization of patrols related selectors in src/selectors/patrols.js.
4- The rest of the files are translations and selector naming updates.

@@ -369,12 +369,6 @@ const Map = ({
);
}, [dispatch]);

const onPatrolTrackLegendClose = useCallback(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dispatch is now handled directly by PatrolTrackLegend.

@@ -1,110 +1,65 @@
import React, { memo, useCallback } from 'react';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation of the new TrackLegend component for patrols:
image

patrolData={patrolData}
/>}
/> : null;
const patrolTrackState = useSelector((state) => state.view.patrolTrackState);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extremely simple component, we just need to select the patrolTrackState and patrolsWithTrackData values from Redux to calculate the description and the items array (patrols being tracked).

@@ -1,125 +1,65 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a noticeable change here, but instead of implementing the legend directly here, now we implement the new TrackLegend component passing down the subjects:
image

const dispatch = useDispatch();
const { t } = useTranslation('tracks', { keyPrefix: 'subjectTrackLegend' });

const timeOfDayTrackingEnabled = useFeatureFlag(FEATURE_FLAG_LABELS.TIME_OF_DAY_TRACKING);

const isTimeOfDayColoringActive = useSelector((state) => state.view.trackSettings.isTimeOfDayColoringActive);
const subjectStore = useSelector((state) => state.data.subjectStore);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation is very similar to the PatrolTrackLegend. Here we select subjectStore, subjectTrackState, subjectTracksTrimmedToTrackTimeEnvelope and trackTimeEnvelope to build the description and items (tracked subjects).

@@ -0,0 +1,57 @@
import React from 'react';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really a new component, it's just the abstraction of what used to be SubjectTrackList so now it's agnostic of the items that are listed. They could either be subjects or patrols.

@@ -0,0 +1,204 @@
import React, { useCallback, useEffect, useState } from 'react';
Copy link
Contributor Author

@luixlive luixlive Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new abstraction that renders track legends. It needs to be implemented and provided with the items, description, callbacks and the flags to render or not certain buttons. SubjectTrackLegend and PatrolTrackLegend use this component to render their legends, they provide subjects and patrols as items respectevely.

The code is very similar to what used to be SubjectTrackLegend, but I removed all the logic related to subjects so now the "items" are injected as dependencies (props) and the component stays agnostic.

results: [],
};

const patrolsReducer = (state = INITIAL_PATROLS_STATE, action) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reducer wasn't needed anymore. We can rely entirely on the patrolStore. And honestly, it was old nasty code, we were basically storing an Axios response object in our store and barely using it (nothing we couldn't achieve using the patrolStore directly).

@@ -1,155 +1,143 @@
import uniq from 'lodash/uniq';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Total refactor and optimization of our patrol selectors.

@@ -228,15 +228,6 @@ export const actualEndTimeForPatrol = (patrol) => {
: null;
};

export const getLeaderForPatrol = (patrol, subjectStore) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused code.

@@ -64,68 +62,45 @@ export const selectHeatmapSubjectTracksTrimmedToTrackTimeEnvelope = createSelect
)
);

const selectPatrolTracksLeaderIds = createSelector(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This selector is no longer needed since the logic to show / hide the subjects tracks when a patrol is being tracked and the subject is the leader of the patrol was a little bit cleaned (lots could be done to improve this flow still, it's buggy, but this is a start).

@luixlive luixlive marked this pull request as ready for review March 3, 2025 20:23
Copy link
Contributor

@gaboDev gaboDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just left a couple of minor comments, the only reason I'm not approving is that I don't see test coverage for TrackLegend implementation in PatrolTrackLegend and SubjectTrackLegend is there a reason for that?

} else {
// If there is a menu expanded, we first collapse it and then expand the new one.
onCollapseMenu();
setTimeout(() => setExpandedMenu(menu), BOOTSTRAP_DEFAULTS.COLLAPSE_TRANSITION_TIME);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be worth it to clear this timeout on unmount?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it's impossible to unmount the component without first finishing the timeout. If you go to the last few lines of this component, it is wrapped by DelayedUnmount by the same amount of time of this timeout: BOOTSTRAP_DEFAULTS.COLLAPSE_TRANSITION_TIME. This means that even if the component is about to be unmounted, it will at least wait for this time to elapse, making the cleaning task unnecessary 😄

onExpandMenu(MENUS.TIME_OF_DAY_SETTINGS);
};

const onDectivateTimeOfDayColoring = useCallback(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little type in here, it should be onDeactivateTimeOfDayColoring

dispatch(setIsTimeOfDayColoringActive(false));

// When deactivating the time of day coloring, we collapse its menu if it was expanded.
if (expandedMenu === MENUS.TIME_OF_DAY_SETTINGS) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several ifs statements are repeating the same comparisons of the const at the beginning of the component isTimeOfDaySettingsExpanded isTrackSettingsExpanded isTracksListExpanded Why are we using those here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duh, didn't think of it 🤓 Updating them!

};
// Build the items array with the description, icon, id and title of each tracked subject.
const items = useMemo(() => subjectTracksTrimmedToTrackTimeEnvelope.map((subjectTracks) => {
const id = subjectTracks.track.features[0].properties.id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here seems like a good place to use:
const [firstFeature] = subjectTracks.track.features;
instead of repeating subjectTracks.track.features[0] just to make the code more readable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is it possible to subjectTracks.track.features[0] to does not exist? If yes, We should add some default values or validations

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the first comment: agreed, I'll update it.
On the second one: it shouldn't, that data comes from a selector that is already running validations and filtering just the valid data. If we ever hit that issue, I wouldn't say the solution is adding a check here but fixing the selector.

@luixlive
Copy link
Contributor Author

luixlive commented Mar 4, 2025

Just left a couple of minor comments, the only reason I'm not approving is that I don't see test coverage for TrackLegend implementation in PatrolTrackLegend and SubjectTrackLegend is there a reason for that?

@gaboDev Nice observation! Actually I forgot to add a comment regarding this myself. I decided not to add tests to those. I tried, but the necessary mocked data in the store for the selectors to build the right scenarios is so complex and big, that the tests would be extremely fragile and hard to comprehend. I know this is not orthodox, we should try to add tests anyway, but for real, the amount of data was insane, and in every scenario it changes considerably. In my opinion, components shouldn't rely on these super complex data selection. Our selectors are doing too much. I would say that the right fix is to update our reducers to actually do some pre-processing before storing the data and avoid having to do all that processing in our selectors. That would optimize time and memory, while also simplifying the data mocking in the tests. But that was definitely out of the scope. This PR was big enough.

@luixlive luixlive requested a review from gaboDev March 4, 2025 21:48
@luixlive luixlive merged commit 89873a4 into develop Mar 4, 2025
3 checks passed
@luixlive luixlive deleted the ERA-11239 branch March 4, 2025 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants