Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 48690561

Přidáno uživatelem Václav Honzík před asi 2 roky(ů)

smol refactor

re #9741

Zobrazit rozdíly:

frontend/src/features/TrackingTool/trackingToolSlice.ts
1
import { createSlice } from "@reduxjs/toolkit"
2
import { LatLngTuple } from "leaflet"
3
import mapConfig from "../../config/mapConfig"
4
import { PathDto } from "../../swagger/data-contracts"
5
import buildPathVariants from "./Map/pathUtils"
6
import { isMapPointDisplayable, MapPoint, PathVariant } from "./trackingToolUtils"
7
import { sendTextForProcessing } from "./trackingToolThunks"
8
import storage from "redux-persist/lib/storage"
1
import { createSlice } from '@reduxjs/toolkit'
2
import mapConfig from '../../config/mapConfig'
3
import { PathDto } from '../../swagger/data-contracts'
4
import buildPathVariants from './Map/mapUtils'
9 5
import TrackingToolState from './trackingToolState'
10

  
11

  
12
const defaultPathsPerPage = 5
6
import { sendTextForProcessing } from './trackingToolThunks'
7
import { calculateMapCenter, MapPoint, PathVariant } from './trackingToolUtils'
13 8

  
14 9
const initialState: TrackingToolState = {
15 10
    isLoading: false,
16 11
    mapCenter: [mapConfig.defaultCoordinates[0], mapConfig.defaultCoordinates[1]],
17
    primaryPathIdx: 0,
12
    displayedPathIdx: 0,
18 13
    dialogApiCallSuccess: true,
19
    pathsPerPage: defaultPathsPerPage,
20
    currentPage: 0,
21
}
22

  
23
const calculateMapCenter = (pathVariant: PathVariant): LatLngTuple | undefined => {
24
    const displayableItems = pathVariant.filter((item) => isMapPointDisplayable(item))
25
    if (displayableItems.length === 0) {
26
        return undefined
27
    }
28

  
29
    return [
30
        displayableItems
31
            .map((item) => item.catalogItem.latitude ?? 0)
32
            .reduce((a, b) => a + b, 0) / displayableItems.length,
33
        displayableItems
34
            .map((item) => item.catalogItem.longitude ?? 0)
35
            .reduce((a, b) => a + b, 0) / displayableItems.length,
36
    ]
37
}
38

  
39
const persistConfig = {
40
    key: "auth",
41
    storage, // localStorage for browsers
42 14
}
43 15

  
44 16
export const trackingToolSlice = createSlice({
45
    name: "trackingTool",
17
    name: 'trackingTool',
46 18
    initialState,
47 19
    reducers: {
48
        consumeErr: (state: TrackingToolState) => ({
49
            ...state,
50
            lastErr: undefined,
51
        }),
52
        setPrimaryIdx: (state: TrackingToolState, action: any) => ({
53
            ...state,
54
            primaryPathIdx: action.payload,
55
        }),
56
        resetDialogApiCallSuccess: (state: TrackingToolState) => ({
57
            ...state,
58
            dialogApiCallSuccess: false,
59
        }),
60
        setPage: (state: TrackingToolState, action: { payload: number }) => ({
61
            ...state,
62
            currentPage: action.payload,
63
        }),
64
        updatePrimaryPath: (state: TrackingToolState, action: { payload: PathVariant }) => {
65
            const { primaryPathIdx } = state
66
            const path = action.payload
67
            const paths = [...state.pathVariants ?? []]
68

  
69
            if (paths.length <= primaryPathIdx) {
70
                return { ...state }
71
            }
72

  
73
            return {
74
                ...state,
75
                pathVariants: [
76
                    ...paths.slice(0, primaryPathIdx),
77
                    path,
78
                    ...paths.slice(primaryPathIdx + 1),
79
                ],
80
            }
81
        },
82
        // Updates map marker while ignoring its idx property
83
        updateMapMarkerWithId: (state: TrackingToolState, action: { payload: { id: string, item: MapPoint } }) => {
84
            const { item } = action.payload
85
            const { primaryPathIdx } = state
86
            if (!state.pathVariants || state.pathVariants.length <= primaryPathIdx) {
87
                return state
88
            }
89

  
90
            const mapMarkerIdx = state.pathVariants[primaryPathIdx].findIndex((item) => item.id === action.payload.id)
91
            if (mapMarkerIdx === -1) {
92
                return state
93
            }
94

  
95
            const newPathVariant = [...state.pathVariants[primaryPathIdx]]
96
            newPathVariant[mapMarkerIdx] = item
97
            return {
98
                ...state,
99
                pathVariants: [...state.pathVariants.slice(0, primaryPathIdx), newPathVariant, ...state.pathVariants.slice(primaryPathIdx + 1)],
100
            }
101
        },
102
        // Updates map marker based on its idx property
20
        consumeErr: (state: TrackingToolState) => ({ ...state, lastErr: undefined, }),
21
        setSelectedPathIdx: (state: TrackingToolState, action: { payload: number }) => ({ ...state, selectedPathIdx: action.payload, }),
22
        resetDialogApiCallSuccess: (state: TrackingToolState) => ({ ...state, dialogApiCallSuccess: false, }),
23
        // Updates currently displayed path
24
        updateDisplayedPath: (state: TrackingToolState, action: { payload: PathVariant }) => ({ ...state, displayedPath: action.payload, }),
25
        // Updates map marker
103 26
        updateMapMarker: (state: TrackingToolState, action: { payload: MapPoint }) => {
104
            const item = action.payload
105
            const { primaryPathIdx } = state
106
            if (!state.pathVariants || state.pathVariants.length <= primaryPathIdx) {
107
                return state
27
            const newPath = [...state.displayedPath ?? []]
28
            if (newPath.length <= action.payload.idx) {
29
                return state // do nothing if there is no path
108 30
            }
109 31

  
32
            newPath[action.payload.idx] = action.payload
110 33
            return {
111 34
                ...state,
112
                pathVariants: state.pathVariants.map((pathVariant, i) => {
113
                    if (i !== primaryPathIdx) {
114
                        return [...pathVariant]
115
                    }
116

  
117
                    return [
118
                        ...pathVariant.slice(0, item.idx),
119
                        item,
120
                        ...pathVariant.slice(item.idx + 1),
121
                    ]
122
                })
35
                displayedPath: newPath,
123 36
            }
124 37
        },
125
        // Removes map marker based on its idx property
38
        // Removes map marker from currently used path
126 39
        removeMapMarker: (state: TrackingToolState, action: { payload: { id: string, idx: number } }) => {
127
            const item = action.payload
128
            const idx = state.primaryPathIdx
129
            if (!state.pathVariants || state.pathVariants.length <= idx) {
130
                return state
40
            const newPath = [...state.displayedPath ?? []]
41
            if (newPath.length <= action.payload.idx) {
42
                return state // do nothing if there is no path
131 43
            }
132 44

  
133 45
            return {
134 46
                ...state,
135
                pathVariants: state.pathVariants.map((pathVariant, i) => {
136
                    if (i !== idx) {
137
                        return [...pathVariant]
138
                    }
139

  
140
                    return [
141
                        ...pathVariant.slice(0, item.idx),
142
                        ...pathVariant.slice(item.idx + 1),
143
                    ]
144
                })
47
                displayedPath: [...newPath.slice(0, action.payload.idx), ...newPath.slice(action.payload.idx + 1)],
145 48
            }
146 49
        },
50
        // Moves map marker in the path array
147 51
        moveMarkerToDestination: (state: TrackingToolState, action: { payload: { destination: number, source: number } }) => {
148 52
            const { destination, source } = action.payload
149
            if (!state.pathVariants || state.pathVariants.length === 0) {
150
                return state
53
            const newPath = [...state.displayedPath ?? []]
54
            if (newPath.length <= source
55
                || newPath.length <= destination
56
                || source === destination) {
57
                return state // do nothing if there is no path
151 58
            }
152 59

  
60
            // JS array dark magic
61
            const [removed] = newPath.splice(action.payload.source, 1)
62
            newPath.splice(destination, 0, removed)
153 63
            return {
154 64
                ...state,
155
                pathVariants: state.pathVariants.map((pathVariant, i) => {
156
                    if (state.primaryPathIdx !== i) {
157
                        return [...pathVariant]
158
                    }
159

  
160
                    if (pathVariant.length <= destination || pathVariant.length <= source) {
161
                        return [...pathVariant]
162
                    }
163

  
164
                    // JS dark magic splice
165
                    const result = [...pathVariant]
166
                    const [removed] = result.splice(source, 1)
167
                    result.splice(destination, 0, removed)
168
                    return result.map((item, idx) => ({ ...item, idx }))
169
                })
65
                displayedPath: newPath.map((item, idx) => ({ ...item, idx })),
170 66
            }
171 67
        },
172 68
        clear: () => ({ ...initialState }),
173 69
        mergeWithCurrentPath: (state: TrackingToolState, action: { payload: PathVariant }) => {
174 70
            const { payload: jsonPath } = action
175 71
            if (!jsonPath) {
176
                return { ...state }
177
            }
178

  
179
            const pathVariants = [...state.pathVariants ?? []]
180
            let primaryPathIdx = state.primaryPathIdx
181
            let currentPage = state.currentPage
182

  
183
            // If there are no path append a new array to the pathVariants array and set primaryIdx to 0
184
            if (pathVariants.length === 0) {
185
                primaryPathIdx = 0
186
                currentPage = 0
187
                pathVariants.push([])
72
                return state
188 73
            }
189 74

  
190
            // Get the path and create a map to check whether some point with the same id already exists
191
            const path = pathVariants[primaryPathIdx]
192
            const pathMap = new Map(path.map((item) => [item.catalogItem.id as string, item]))
75
            const newPath = [...state.displayedPath ?? []]
76
            const existingIds = new Set(newPath.map(item => item.id ?? ''))
193 77

  
194
            // Create an array of items to be replaced and items to be added to the end
195
            // const itemsToReplace: MapPoint[] = []
196
            const itemsToAdd: MapPoint[] = []
197
            jsonPath.forEach((item) => {
198
                if (!pathMap.has(item.catalogItem.id ?? '')) {
199
                    itemsToAdd.push(item)
200
                    return
78
            const newItems: MapPoint[] = []
79
            jsonPath.forEach(item => {
80
                if (!existingIds.has(item.id ?? '')) {
81
                    newItems.push(item)
201 82
                }
202

  
203
                // const idx = pathMap.get(item.catalogItem.id as string)!.idx
204
                // item.idx = idx
205
                // itemsToReplace.push(item)
206 83
            })
207 84

  
208
            // Iterate over items to replace and replace them
209
            const newPath = [...path]
210
            // itemsToReplace.forEach((item) => {
211
            //     newPath[item.idx] = item
212
            // })
213

  
214
            // Add items to the end
215
            itemsToAdd.forEach((item) => {
85
            newItems.forEach(item => {
216 86
                item.addToPath = !state.pathVariants || state.pathVariants.length === 0
217 87
                item.idx = newPath.length
218 88
                newPath.push(item)
219 89
            })
220 90

  
221
            // Return the new path
222 91
            return {
223 92
                ...state,
224
                pathVariants: [
225
                    ...pathVariants.slice(0, primaryPathIdx),
226
                    newPath,
227
                    ...pathVariants.slice(primaryPathIdx + 1),
228
                ],
229
                primaryPathIdx, // in case the list is empty
230
                currentPage, // in case the list is empty
93
                displayedPath: newPath,
231 94
            }
232
        }
95
        },
233 96
    },
234 97
    extraReducers: (builder) => {
235 98
        builder.addCase(sendTextForProcessing.fulfilled, (state, action) => {
236 99
            const pathDto: PathDto = action.payload
237 100
            const pathVariants = buildPathVariants(pathDto)
101
            const selectedPath = pathVariants.length > 0 ? pathVariants[0] : undefined
102
            if (!selectedPath) {
103
                return {
104
                    ...state,
105
                    pathDto,
106
                    pathVariants,
107
                    displayedPath: undefined,
108
                    displayedPathIdx: 0,
109
                    dialogApiCallSuccess: true,
110
                    isLoading: false,
111
                }
112
            }
238 113

  
239
            const mapCenter = calculateMapCenter(pathVariants[state.primaryPathIdx])
114
            const mapCenter = calculateMapCenter(selectedPath)
240 115
            return {
241 116
                ...state,
242 117
                pathVariants,
......
245 120
                isLoading: false,
246 121
                dialogApiCallSuccess: true,
247 122
                currentPage: 0,
123
                displayedPath: selectedPath,
124
                displayedPathIdx: 0,
248 125
            }
249 126
        })
250 127
        builder.addCase(sendTextForProcessing.rejected, (_, action) => ({
......
253 130
            isLoading: false,
254 131
            dialogApiCallSuccess: false,
255 132
            currentPage: 0,
133
            displayedPath: undefined,
134
            displayedPathIdx: 0,
135
            pathDto: undefined,
136
            pathVariants: undefined,
256 137
        }))
257
        builder.addCase(sendTextForProcessing.pending, (state) => {
258
            return {
259
                ...state,
260
                isLoading: true,
261
                dialogApiCallSuccess: false,
262
            }
263
        })
264
    },
138
        builder.addCase(sendTextForProcessing.pending, (state) => ({
139
            ...state,
140
            isLoading: true,
141
            dialogApiCallSuccess: false,
142
        }))
143
    }
265 144
})
266 145

  
267 146
export const {
268 147
    consumeErr,
269
    setPrimaryIdx,
148
    setSelectedPathIdx,
270 149
    resetDialogApiCallSuccess,
271
    clear,
150
    updateDisplayedPath,
272 151
    updateMapMarker,
273
    mergeWithCurrentPath,
274
    moveMarkerToDestination,
275
    updateMapMarkerWithId,
276 152
    removeMapMarker,
277
    updatePrimaryPath
153
    moveMarkerToDestination,
154
    clear,
155
    mergeWithCurrentPath,
278 156
} = trackingToolSlice.actions
157

  
279 158
const trackingToolReducer = trackingToolSlice.reducer
280 159
export default trackingToolReducer

Také k dispozici: Unified diff