Skip to content

Commit

Permalink
wip: move to vesselsAdapter
Browse files Browse the repository at this point in the history
  • Loading branch information
louptheron committed Oct 4, 2024
1 parent ebfcb43 commit d33441f
Show file tree
Hide file tree
Showing 14 changed files with 120 additions and 104 deletions.
3 changes: 0 additions & 3 deletions frontend/src/domain/entities/vessel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,10 @@ export type TrackTypeRecordItem = {

export type VesselEnhancedLastPositionWebGLObject = {
coordinates: number[]
course: number
filterPreview: number // 0 is False, 1 is True - for WebGL
hasBeaconMalfunction: boolean
isAtPort: boolean
isFiltered: number // 0 is False, 1 is True - for WebGL
lastPositionSentAt: number
speed: number
vesselFeatureId: VesselFeatureId
vesselProperties: Vessel.VesselEnhancedObject
}
Expand Down
152 changes: 70 additions & 82 deletions frontend/src/domain/shared_slices/Vessel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { reportingIsAnInfractionSuspicion } from '@features/Reporting/utils'
import { createSlice } from '@reduxjs/toolkit'
import { transform } from 'ol/proj'
import {reportingIsAnInfractionSuspicion} from '@features/Reporting/utils'

Check failure on line 1 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

There should be at least one empty line between import groups

Check failure on line 1 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

Replace `reportingIsAnInfractionSuspicion` with `·reportingIsAnInfractionSuspicion·`
import type {PayloadAction} from '@reduxjs/toolkit'

Check failure on line 2 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

There should be at least one empty line between import groups

Check failure on line 2 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

`@reduxjs/toolkit` type import should occur after type import of `@features/Vessel/Vessel.types`

Check failure on line 2 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

Replace `PayloadAction` with `·PayloadAction·`

Check failure on line 2 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

'/home/runner/work/monitorfish/monitorfish/frontend/node_modules/@reduxjs/toolkit/dist/redux-toolkit.legacy-esm.js' imported multiple times
import {createEntityAdapter, createSlice, type EntityState} from '@reduxjs/toolkit'

Check failure on line 3 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

Replace `createEntityAdapter,·createSlice,·type·EntityState` with `·createEntityAdapter,·createSlice,·type·EntityState·`

Check failure on line 3 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

'/home/runner/work/monitorfish/monitorfish/frontend/node_modules/@reduxjs/toolkit/dist/redux-toolkit.legacy-esm.js' imported multiple times
import {transform} from 'ol/proj'

Check failure on line 4 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

Replace `transform` with `·transform·`

import { ReportingType, ReportingTypeCharacteristics } from '../../features/Reporting/types'
import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../entities/map/constants'
import {ReportingType, ReportingTypeCharacteristics} from '../../features/Reporting/types'

Check failure on line 6 in frontend/src/domain/shared_slices/Vessel.ts

View workflow job for this annotation

GitHub Actions / Run frontend unit tests

Replace `ReportingType,·ReportingTypeCharacteristics` with `·ReportingType,·ReportingTypeCharacteristics·`
import {OPENLAYERS_PROJECTION, WSG84_PROJECTION} from '../entities/map/constants'
import {
atLeastOneVesselSelected,
getOnlyVesselIdentityProperties,
Expand All @@ -18,41 +19,17 @@ import type {
VesselEnhancedLastPositionWebGLObject,
VesselFeatureId,
VesselIdentity,
VesselLastPosition,
VesselPosition
} from '../entities/vessel/types'
import type { Vessel as VesselTypes } from '@features/Vessel/Vessel.types'
import type { PayloadAction } from '@reduxjs/toolkit'
import type {Vessel as VesselTypes} from '@features/Vessel/Vessel.types'

const NOT_FOUND = -1

function filterFirstFoundReportingType(reportingType) {
let reportingTypeHasBeenRemoved = false

return (acc, returnedReportingType) => {
if (returnedReportingType === reportingType && !reportingTypeHasBeenRemoved) {
reportingTypeHasBeenRemoved = true

return acc
}

acc.push(returnedReportingType)

return acc
}
}

function filterFirstFoundReportingTypes(reportingTypes, vesselReportingsToRemove) {
let vesselReportingWithoutFirstFoundReportingTypes = reportingTypes

vesselReportingsToRemove.forEach(reportingToRemove => {
vesselReportingWithoutFirstFoundReportingTypes = vesselReportingWithoutFirstFoundReportingTypes.reduce(
filterFirstFoundReportingType(reportingToRemove.type),
[]
)
})

return vesselReportingWithoutFirstFoundReportingTypes
}
export const vesselsAdapter = createEntityAdapter({
selectId: (vessel: VesselEnhancedLastPositionWebGLObject) => vessel.vesselFeatureId,
sortComparer: false
});

// TODO Properly type this redux state.
export type VesselState = {
Expand All @@ -72,7 +49,7 @@ export type VesselState = {
vesselSidebarIsOpen: boolean
vesselSidebarTab: VesselSidebarTab
vesselTrackExtent: any | null
vessels: VesselEnhancedLastPositionWebGLObject[]
vessels: EntityState<VesselEnhancedLastPositionWebGLObject, VesselFeatureId>
vesselsEstimatedPositions: any[]
vesselsTracksShowed: Record<string, ShowedVesselTrack>
}
Expand All @@ -90,7 +67,7 @@ const INITIAL_STATE: VesselState = {
tripMessagesLastToFormerDEPDateTimes: [],
uniqueVesselsDistricts: [],
uniqueVesselsSpecies: [],
vessels: [],
vessels: vesselsAdapter.getInitialState(),
vesselsEstimatedPositions: [],
vesselSidebarIsOpen: false,
vesselSidebarTab: VesselSidebarTab.SUMMARY,
Expand Down Expand Up @@ -406,48 +383,32 @@ const vesselSlice = createSlice({
state.selectedVesselPositions = null
},

/**
* Reset the vessel track features extent
* @function setVesselTrackExtent
*
* @param {Object} state
*/
resetVesselTrackExtent(state) {
state.vesselTrackExtent = null
},

setAllVesselsAsUnfiltered(state) {
if (!state.vessels.find(vessel => vessel.isFiltered)) {
return
// Check if any vessel has `isFiltered` set to true
if (!Object.values(state.vessels).some(vessel => vessel.isFiltered)) {
return;
}

state.vessels = state.vessels.map(vessel => ({
...vessel,
isFiltered: 0
}))
// Update all vessels' `isFiltered` field to 0
vesselsAdapter.updateAll(state.vessels, {
changes: { isFiltered: 0 }
})
},

/**
* Set filtered features as true
* @function setFilteredVesselsFeatures
* @param {Object} state
* @param {{payload: string[]}} action - the vessel features uids
*/
setFilteredVesselsFeatures(state, action) {
setFilteredVesselsFeatures(state, action: PayloadAction<VesselFeatureId>) {
const filteredVesselsFeaturesUids = action.payload
state.vessels = state.vessels.map(vessel => {
if (filteredVesselsFeaturesUids.indexOf(vessel.vesselFeatureId) !== NOT_FOUND) {
return {
...vessel,
isFiltered: 1
const vesselIds = vesselsAdapter.getSelectors().selectIds(state.vessels)

// Update only the vessels that match the filtered IDs
vesselsAdapter.updateMany(
state.vessels,
vesselIds.map(vesselId => ({
id: vesselId,
changes: {
isFiltered: filteredVesselsFeaturesUids.includes(vesselId) ? 1 : 0
}
}

return {
...vessel,
isFiltered: 0
}
})
}))
)
},

/**
Expand Down Expand Up @@ -525,19 +486,18 @@ const vesselSlice = createSlice({
state.vesselsEstimatedPositions = action.payload
},

setVesselsFromAPI(state, action) {
// FIXME : find a way to update state.vessel[vessels] without overriding
// "private" properties like isFiltered / filterPreview when uploading from api
state.vessels = action.payload?.map(vessel => ({
setVesselsFromAPI(state, action: PayloadAction<VesselLastPosition>) {
if (!action.payload || !Array.isArray(action.payload)) {
return
}

vesselsAdapter.setMany(state.vessels, action.payload.map(vessel => ({
vesselFeatureId: Vessel.getVesselFeatureId(vessel),
coordinates: transform([vessel.longitude, vessel.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION),
course: vessel.course,
filterPreview: 0,
hasBeaconMalfunction: !!vessel.beaconMalfunctionId,
isAtPort: vessel.isAtPort,
isFiltered: 0,
lastPositionSentAt: new Date(vessel.dateTime).getTime(),
speed: vessel.speed,
vesselFeatureId: Vessel.getVesselFeatureId(vessel),
vesselProperties: {
...vessel,
flagState: vessel.flagState,
Expand All @@ -553,7 +513,7 @@ const vesselSlice = createSlice({
? Array.from(new Set(vessel.speciesOnboard.map(species => species.species)))
: []
}
}))
})));
},

setVesselsSpeciesAndDistricts(state, action) {
Expand Down Expand Up @@ -659,6 +619,35 @@ const vesselSlice = createSlice({
}
})

function filterFirstFoundReportingType(reportingType) {
let reportingTypeHasBeenRemoved = false

return (acc, returnedReportingType) => {
if (returnedReportingType === reportingType && !reportingTypeHasBeenRemoved) {
reportingTypeHasBeenRemoved = true

return acc
}

acc.push(returnedReportingType)

return acc
}
}

function filterFirstFoundReportingTypes(reportingTypes, vesselReportingsToRemove) {
let vesselReportingWithoutFirstFoundReportingTypes = reportingTypes

vesselReportingsToRemove.forEach(reportingToRemove => {
vesselReportingWithoutFirstFoundReportingTypes = vesselReportingWithoutFirstFoundReportingTypes.reduce(
filterFirstFoundReportingType(reportingToRemove.type),
[]
)
})

return vesselReportingWithoutFirstFoundReportingTypes
}

export const {
addVesselReporting,
addVesselTrackShowed,
Expand All @@ -671,7 +660,6 @@ export const {
resetHighlightedVesselTrackPosition,
resetLoadingVessel,
resetSelectedVessel,
resetVesselTrackExtent,
setAllVesselsAsUnfiltered,
setFilteredVesselsFeatures,
setHideNonSelectedVessels,
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/domain/use_cases/vessel/showVessel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { displayedComponentActions } from '../../shared_slices/DisplayedComponen
import { displayedErrorActions } from '../../shared_slices/DisplayedError'
import { addSearchedVessel, removeError, setError } from '../../shared_slices/Global'
import { doNotAnimate } from '../../shared_slices/Map'
import { loadingVessel, resetLoadingVessel, setSelectedVessel } from '../../shared_slices/Vessel'
import {loadingVessel, resetLoadingVessel, setSelectedVessel, vesselsAdapter} from '../../shared_slices/Vessel'
import { displayOrLogError } from '../error/displayOrLogError'

import type { VesselIdentity } from '../../entities/vessel/types'
Expand All @@ -22,7 +22,10 @@ export const showVessel =
(vesselIdentity: VesselIdentity, isFromSearch: boolean, isFromUserAction: boolean) => async (dispatch, getState) => {
try {
const { fishingActivities, map, vessel } = getState()
const { selectedVesselTrackRequest, vessels } = vessel
const selectedVesselTrackRequest = vessel.selectedVesselTrackRequest
const vesselsSelector = vessel.vessels
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)
console.log(vessels)
const { defaultVesselTrackDepth } = map
const { areFishingActivitiesShowedOnMap } = fishingActivities
// TODO How to handle both the control unit dialog and the vessel sidebar ?
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/features/Vessel/useCases/applyFilterToVessels.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { setError } from '../../../domain/shared_slices/Global'
import { setAllVesselsAsUnfiltered, setFilteredVesselsFeatures } from '../../../domain/shared_slices/Vessel'
import {
setAllVesselsAsUnfiltered,
setFilteredVesselsFeatures,
vesselsAdapter
} from '../../../domain/shared_slices/Vessel'
import { getFilteredVessels } from '../../../domain/use_cases/vessel/getFilteredVessels'
import NoVesselsInFilterError from '../../../errors/NoVesselsInFilterError'

import type { MainAppThunk } from '@store'

export const applyFilterToVessels = (): MainAppThunk => (dispatch, getState) => {
const showedFilter = getState().filter?.filters?.find(filter => filter.showed)
const { vessels } = getState().vessel
const vesselsSelector = getState().vessel.vessels
if (!vesselsSelector) {
return
}
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)
if (!showedFilter) {
return dispatch(setAllVesselsAsUnfiltered())
}
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/features/VesselList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { setDisplayedComponents } from '../../domain/shared_slices/DisplayedComp
import { addFilter } from '../../domain/shared_slices/Filter'
import { setBlockVesselsUpdate, setPreviewFilteredVesselsMode } from '../../domain/shared_slices/Global'
import { animateToExtent } from '../../domain/shared_slices/Map'
import { setPreviewFilteredVesselsFeatures } from '../../domain/shared_slices/Vessel'
import {setPreviewFilteredVesselsFeatures, vesselsAdapter} from '../../domain/shared_slices/Vessel'
import { addVesselListFilterZone } from '../../domain/use_cases/vessel/addVesselListFilterZone'
import { getFilteredVessels } from '../../domain/use_cases/vessel/getFilteredVessels'
import { unselectVessel } from '../../domain/use_cases/vessel/unselectVessel'
Expand Down Expand Up @@ -70,8 +70,9 @@ export function VesselList({ namespace }) {
const {
uniqueVesselsDistricts: districts,
uniqueVesselsSpecies: species,
vessels
} = useMainAppSelector(state => state.vessel)
const vesselsSelector = useMainAppSelector(state => state.vessel.vessels)
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)
const getFleetSegmentsQuery = useGetFleetSegmentsQuery()
const gears = useMainAppSelector(state => state.gear.gears)

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/features/VesselSearch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { showVessel } from '../../domain/use_cases/vessel/showVessel'
import type { VesselIdentity } from '../../domain/entities/vessel/types'
import type { ChangeEvent, InputHTMLAttributes, MutableRefObject } from 'react'
import type { Promisable } from 'type-fest'
import {vesselsAdapter} from "../../domain/shared_slices/Vessel";

type VesselSearchProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'onChange'> & {
baseRef?: MutableRefObject<HTMLDivElement | undefined> | undefined
Expand Down Expand Up @@ -51,7 +52,8 @@ export function VesselSearch({
const dispatch = useMainAppDispatch()
const baseUrl = window.location.origin
const selectedVesselIdentity = useMainAppSelector(state => state.vessel.selectedVesselIdentity)
const vessels = useMainAppSelector(state => state.vessel.vessels)
const vesselsSelector = useMainAppSelector(state => state.vessel.vessels)
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)
const searchQueryRef = useRef('')
const wrapperRef = useRef(null)

Expand Down
6 changes: 4 additions & 2 deletions frontend/src/features/map/MapMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import MapMenuOverlay from './overlays/MapMenuOverlay'
import { HIT_PIXEL_TO_TOLERANCE } from '../../constants/constants'
import { LayerProperties } from '../../domain/entities/layers/constants'
import { MonitorFishLayer } from '../../domain/entities/layers/types'
import { useMainAppSelector } from '../../hooks/useMainAppSelector'

import type { VesselEnhancedLastPositionWebGLObject } from '../../domain/entities/vessel/types'
import {vesselsAdapter} from "../../domain/shared_slices/Vessel";
import {useMainAppSelector} from "@hooks/useMainAppSelector";

export function MapMenu() {
const { vessels } = useMainAppSelector(state => state.vessel)
const vesselsSelector = useMainAppSelector(state => state.vessel.vessels)
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)
const [coordinates, setCoordinates] = useState<number[]>([])
const vessel = useRef<VesselEnhancedLastPositionWebGLObject | undefined>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import { getVesselAlertAndBeaconMalfunctionStyle } from './style'
import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../../domain/entities/vessel/vessel'
import { useIsSuperUser } from '../../../../auth/hooks/useIsSuperUser'
import { monitorfishMap } from '../../monitorfishMap'
import { vesselsAdapter } from '../../../../domain/shared_slices/Vessel'

const VesselAlertAndBeaconMalfunctionLayer = () => {
const isSuperUser = useIsSuperUser()
const {
vessels,
hideNonSelectedVessels,
selectedVesselIdentity,
vesselsTracksShowed
} = useSelector(state => state.vessel)
const vesselsSelector = useSelector(state => state.vessel.vessels)
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)

const {
nonFilteredVesselsAreHidden
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/features/map/layers/Vessel/VesselAlertLayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ import {
} from '../../../../domain/entities/vessel/vessel'
import { useIsSuperUser } from '../../../../auth/hooks/useIsSuperUser'
import { monitorfishMap } from '../../monitorfishMap'
import { vesselsAdapter } from '../../../../domain/shared_slices/Vessel'

const VesselAlertLayer = () => {
const isSuperUser = useIsSuperUser()

const {
vessels,
hideNonSelectedVessels,
selectedVesselIdentity,
vesselsTracksShowed
} = useSelector(state => state.vessel)
const vesselsSelector = useSelector(state => state.vessel.vessels)
const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector)

const {
nonFilteredVesselsAreHidden
Expand Down
Loading

0 comments on commit d33441f

Please sign in to comment.