Projekt

Obecné

Profil

Stáhnout (8.45 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, { isMapPointDisplayable, MapPoint, PathVariant } from "./Map/pathUtils"
6
import { sendTextForProcessing } from "./trackingToolThunks"
7
import storage from "redux-persist/lib/storage"
8
import TrackingToolState from './TrackingToolState'
9

    
10

    
11
const defaultPathsPerPage = 5
12

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

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

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

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

    
43
export const trackingToolSlice = createSlice({
44
    name: "trackingTool",
45
    initialState,
46
    reducers: {
47
        consumeErr: (state: TrackingToolState) => ({
48
            ...state,
49
            lastErr: undefined,
50
        }),
51
        setPrimaryIdx: (state: TrackingToolState, action: any) => ({
52
            ...state,
53
            primaryPathIdx: action.payload,
54
        }),
55
        resetDialogApiCallSuccess: (state: TrackingToolState) => ({
56
            ...state,
57
            dialogApiCallSuccess: false,
58
        }),
59
        setPage: (state: TrackingToolState, action: { payload: number }) => ({
60
            ...state,
61
            currentPage: action.payload,
62
        }),
63
        // Updates map marker while ignoring its idx property
64
        updateMapMarkerWithId: (state: TrackingToolState, action: { payload: { id: string, item: MapPoint } }) => {
65
            const { item } = action.payload
66
            const idx = state.primaryPathIdx
67
            if (!state.pathVariants || state.pathVariants.length <= idx) {
68
                return state
69
            }
70

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

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

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

    
98
                    return [
99
                        ...pathVariant.slice(0, item.idx),
100
                        item,
101
                        ...pathVariant.slice(item.idx + 1),
102
                    ]
103
                })
104
            }
105
        },
106
        moveMarkerToDestination: (state: TrackingToolState, action: { payload: { destination: number, source: number } }) => {
107
            const { destination, source } = action.payload
108
            if (!state.pathVariants || state.pathVariants.length === 0) {
109
                return state
110
            }
111

    
112
            return {
113
                ...state,
114
                pathVariants: state.pathVariants.map((pathVariant, i) => {
115
                    if (state.primaryPathIdx !== i) {
116
                        return [...pathVariant]
117
                    }
118

    
119
                    if (pathVariant.length <= destination || pathVariant.length <= source) {
120
                        return [...pathVariant]
121
                    }
122

    
123
                    // JS dark magic splice
124
                    const result = [...pathVariant]
125
                    const [removed] = result.splice(source, 1)
126
                    result.splice(destination, 0, removed)
127
                    return result.map((item, idx) => ({ ...item, idx }))
128
                })
129
            }
130
        },
131
        clear: () => ({ ...initialState }),
132
        mergeWithCurrentPath: (state: TrackingToolState, action: { payload: PathVariant }) => {
133
            const { payload: jsonPath } = action
134
            if (!jsonPath) {
135
                return { ...state }
136
            }
137

    
138
            const pathVariants = [...state.pathVariants ?? []]
139
            let primaryPathIdx = state.primaryPathIdx
140
            let currentPage = state.currentPage
141

    
142
            // If there are no path append a new array to the pathVariants array and set primaryIdx to 0
143
            if (pathVariants.length === 0) {
144
                primaryPathIdx = 0
145
                currentPage = 0
146
                pathVariants.push([])
147
            }
148

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

    
153
            // Create an array of items to be replaced and items to be added to the end
154
            // const itemsToReplace: MapPoint[] = []
155
            const itemsToAdd: MapPoint[] = []
156
            jsonPath.forEach((item) => {
157
                if (!pathMap.has(item.catalogItem.id as string)) {
158
                    itemsToAdd.push(item)
159
                    return
160
                }
161

    
162
                // const idx = pathMap.get(item.catalogItem.id as string)!.idx
163
                // item.idx = idx
164
                // itemsToReplace.push(item)
165
            })
166

    
167
            // Iterate over items to replace and replace them
168
            const newPath = [...path]
169
            // itemsToReplace.forEach((item) => {
170
            //     newPath[item.idx] = item
171
            // })
172

    
173
            // Add items to the end
174
            itemsToAdd.forEach((item) => {
175
                item.addToPath = !state.pathVariants || state.pathVariants.length === 0
176
                item.idx = newPath.length
177
                newPath.push(item)
178
            })
179

    
180
            // Return the new path
181
            return {
182
                ...state,
183
                pathVariants: [
184
                    ...pathVariants.slice(0, primaryPathIdx),
185
                    newPath,
186
                    ...pathVariants.slice(primaryPathIdx + 1),
187
                ],
188
                primaryPathIdx, // in case the list is empty
189
                currentPage, // in case the list is empty
190
            }
191
        }
192
    },
193
    extraReducers: (builder) => {
194
        builder.addCase(sendTextForProcessing.fulfilled, (state, action) => {
195
            const pathDto: PathDto = action.payload
196
            const pathVariants = buildPathVariants(pathDto)
197

    
198
            const mapCenter = calculateMapCenter(pathVariants[state.primaryPathIdx])
199
            return {
200
                ...state,
201
                pathVariants,
202
                pathDto,
203
                mapCenter: mapCenter ?? state.mapCenter,
204
                isLoading: false,
205
                dialogApiCallSuccess: true,
206
                currentPage: 0,
207
            }
208
        })
209
        builder.addCase(sendTextForProcessing.rejected, (_, action) => ({
210
            ...initialState,
211
            lastError: action.error.message,
212
            isLoading: false,
213
            dialogApiCallSuccess: false,
214
            currentPage: 0,
215
        }))
216
        builder.addCase(sendTextForProcessing.pending, (state) => {
217
            return {
218
                ...state,
219
                isLoading: true,
220
                dialogApiCallSuccess: false,
221
            }
222
        })
223
    },
224
})
225

    
226
export const {
227
    consumeErr,
228
    setPrimaryIdx,
229
    resetDialogApiCallSuccess,
230
    clear,
231
    updateMapMarker,
232
    mergeWithCurrentPath,
233
    moveMarkerToDestination,
234
    updateMapMarkerWithId
235
} = trackingToolSlice.actions
236
const trackingToolReducer = trackingToolSlice.reducer
237
export default trackingToolReducer
(4-4/5)