Projekt

Obecné

Profil

Stáhnout (9.3 KB) Statistiky
| Větev: | Tag: | Revize:
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"
9
import TrackingToolState from './trackingToolState'
10

    
11

    
12
const defaultPathsPerPage = 5
13

    
14
const initialState: TrackingToolState = {
15
    isLoading: false,
16
    mapCenter: [mapConfig.defaultCoordinates[0], mapConfig.defaultCoordinates[1]],
17
    primaryPathIdx: 0,
18
    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
}
43

    
44
export const trackingToolSlice = createSlice({
45
    name: "trackingTool",
46
    initialState,
47
    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
        // Updates map marker while ignoring its idx property
65
        updateMapMarkerWithId: (state: TrackingToolState, action: { payload: { id: string, item: MapPoint } }) => {
66
            const { item } = action.payload
67
            const idx = state.primaryPathIdx
68
            if (!state.pathVariants || state.pathVariants.length <= idx) {
69
                return state
70
            }
71

    
72
            const mapMarkerIdx = state.pathVariants[idx].findIndex((item) => item.id === action.payload.id)
73
            if (mapMarkerIdx === -1) {
74
                return state
75
            }
76

    
77
            const newPathVariant = [...state.pathVariants[idx]]
78
            newPathVariant[mapMarkerIdx] = item
79
            return {
80
                ...state,
81
                pathVariants: [...state.pathVariants.slice(0, idx), newPathVariant, ...state.pathVariants.slice(idx + 1)],
82
            }
83
        },
84
        // Updates map marker based on its idx property
85
        updateMapMarker: (state: TrackingToolState, action: { payload: MapPoint }) => {
86
            const item = action.payload
87
            const idx = state.primaryPathIdx
88
            if (!state.pathVariants || state.pathVariants.length <= idx) {
89
                return state
90
            }
91

    
92
            return {
93
                ...state,
94
                pathVariants: state.pathVariants.map((pathVariant, i) => {
95
                    if (i !== idx) {
96
                        return [...pathVariant]
97
                    }
98

    
99
                    return [
100
                        ...pathVariant.slice(0, item.idx),
101
                        item,
102
                        ...pathVariant.slice(item.idx + 1),
103
                    ]
104
                })
105
            }
106
        },
107
        // Removes map marker based on its idx property
108
        removeMapMarker: (state: TrackingToolState, action: { payload: { id: string, idx: number } }) => {
109
            const item = action.payload
110
            const idx = state.primaryPathIdx
111
            if (!state.pathVariants || state.pathVariants.length <= idx) {
112
                return state
113
            }
114

    
115
            return {
116
                ...state,
117
                pathVariants: state.pathVariants.map((pathVariant, i) => {
118
                    if (i !== idx) {
119
                        return [...pathVariant]
120
                    }
121

    
122
                    return [
123
                        ...pathVariant.slice(0, item.idx),
124
                        ...pathVariant.slice(item.idx + 1),
125
                    ]
126
                })
127
            }
128
        },
129
        moveMarkerToDestination: (state: TrackingToolState, action: { payload: { destination: number, source: number } }) => {
130
            const { destination, source } = action.payload
131
            if (!state.pathVariants || state.pathVariants.length === 0) {
132
                return state
133
            }
134

    
135
            return {
136
                ...state,
137
                pathVariants: state.pathVariants.map((pathVariant, i) => {
138
                    if (state.primaryPathIdx !== i) {
139
                        return [...pathVariant]
140
                    }
141

    
142
                    if (pathVariant.length <= destination || pathVariant.length <= source) {
143
                        return [...pathVariant]
144
                    }
145

    
146
                    // JS dark magic splice
147
                    const result = [...pathVariant]
148
                    const [removed] = result.splice(source, 1)
149
                    result.splice(destination, 0, removed)
150
                    return result.map((item, idx) => ({ ...item, idx }))
151
                })
152
            }
153
        },
154
        clear: () => ({ ...initialState }),
155
        mergeWithCurrentPath: (state: TrackingToolState, action: { payload: PathVariant }) => {
156
            const { payload: jsonPath } = action
157
            if (!jsonPath) {
158
                return { ...state }
159
            }
160

    
161
            const pathVariants = [...state.pathVariants ?? []]
162
            let primaryPathIdx = state.primaryPathIdx
163
            let currentPage = state.currentPage
164

    
165
            // If there are no path append a new array to the pathVariants array and set primaryIdx to 0
166
            if (pathVariants.length === 0) {
167
                primaryPathIdx = 0
168
                currentPage = 0
169
                pathVariants.push([])
170
            }
171

    
172
            // Get the path and create a map to check whether some point with the same id already exists
173
            const path = pathVariants[primaryPathIdx]
174
            const pathMap = new Map(path.map((item) => [item.catalogItem.id as string, item]))
175

    
176
            // Create an array of items to be replaced and items to be added to the end
177
            // const itemsToReplace: MapPoint[] = []
178
            const itemsToAdd: MapPoint[] = []
179
            jsonPath.forEach((item) => {
180
                if (!pathMap.has(item.catalogItem.id ?? '')) {
181
                    itemsToAdd.push(item)
182
                    return
183
                }
184

    
185
                // const idx = pathMap.get(item.catalogItem.id as string)!.idx
186
                // item.idx = idx
187
                // itemsToReplace.push(item)
188
            })
189

    
190
            // Iterate over items to replace and replace them
191
            const newPath = [...path]
192
            // itemsToReplace.forEach((item) => {
193
            //     newPath[item.idx] = item
194
            // })
195

    
196
            // Add items to the end
197
            itemsToAdd.forEach((item) => {
198
                item.addToPath = !state.pathVariants || state.pathVariants.length === 0
199
                item.idx = newPath.length
200
                newPath.push(item)
201
            })
202

    
203
            // Return the new path
204
            return {
205
                ...state,
206
                pathVariants: [
207
                    ...pathVariants.slice(0, primaryPathIdx),
208
                    newPath,
209
                    ...pathVariants.slice(primaryPathIdx + 1),
210
                ],
211
                primaryPathIdx, // in case the list is empty
212
                currentPage, // in case the list is empty
213
            }
214
        }
215
    },
216
    extraReducers: (builder) => {
217
        builder.addCase(sendTextForProcessing.fulfilled, (state, action) => {
218
            const pathDto: PathDto = action.payload
219
            const pathVariants = buildPathVariants(pathDto)
220

    
221
            const mapCenter = calculateMapCenter(pathVariants[state.primaryPathIdx])
222
            return {
223
                ...state,
224
                pathVariants,
225
                pathDto,
226
                mapCenter: mapCenter ?? state.mapCenter,
227
                isLoading: false,
228
                dialogApiCallSuccess: true,
229
                currentPage: 0,
230
            }
231
        })
232
        builder.addCase(sendTextForProcessing.rejected, (_, action) => ({
233
            ...initialState,
234
            lastError: action.error.message,
235
            isLoading: false,
236
            dialogApiCallSuccess: false,
237
            currentPage: 0,
238
        }))
239
        builder.addCase(sendTextForProcessing.pending, (state) => {
240
            return {
241
                ...state,
242
                isLoading: true,
243
                dialogApiCallSuccess: false,
244
            }
245
        })
246
    },
247
})
248

    
249
export const {
250
    consumeErr,
251
    setPrimaryIdx,
252
    resetDialogApiCallSuccess,
253
    clear,
254
    updateMapMarker,
255
    mergeWithCurrentPath,
256
    moveMarkerToDestination,
257
    updateMapMarkerWithId,
258
    removeMapMarker,
259
} = trackingToolSlice.actions
260
const trackingToolReducer = trackingToolSlice.reducer
261
export default trackingToolReducer
(4-4/6)