/* eslint-disable no-magic-numbers */ import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { highestZIndexSelector, layerByIdSelector, lowestZIndexSelector, } from '@/redux/layers/layers.selectors'; import { JSX, useState } from 'react'; import { LayersDrawerImageItem } from '@/components/Map/Drawer/LayersDrawer/LayersDrawerImageItem.component'; import { LayersDrawerTextItem } from '@/components/Map/Drawer/LayersDrawer/LayerDrawerTextItem.component'; import QuestionModal from '@/components/FunctionalArea/Modal/QuestionModal/QustionModal.component'; import { removeLayerImage, removeLayerText, updateLayerImageObject, updateLayerText, } from '@/redux/layers/layers.thunks'; import { layerDeleteImage, layerDeleteText, layerUpdateImage, layerUpdateText, } from '@/redux/layers/layers.slice'; import removeElementFromLayer from '@/components/Map/MapViewer/utils/shapes/elements/removeElementFromLayer'; import { showToast } from '@/utils/showToast'; import { SerializedError } from '@reduxjs/toolkit'; import { LayerImage, LayerText } from '@/types/models'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useMapInstance } from '@/utils/context/mapInstanceContext'; import { mapModelIdSelector } from '@/redux/map/map.selectors'; import { mapEditToolsSetLayerObject } from '@/redux/mapEditTools/mapEditTools.slice'; import updateElement from '@/components/Map/MapViewer/utils/shapes/layer/utils/updateElement'; import { useSetBounds } from '@/utils/map/useSetBounds'; import { mapEditToolsLayerObjectSelector } from '@/redux/mapEditTools/mapEditTools.selectors'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; import { Coordinate } from 'ol/coordinate'; import { openLayerImageObjectEditFactoryModal, openLayerTextEditFactoryModal, } from '@/redux/modal/modal.slice'; interface LayersDrawerObjectsListProps { layerId: number; isLayerVisible: boolean; isLayerActive: boolean; } const removeObjectConfig = { image: { question: 'Are you sure you want to remove the image?', successMessage: 'The layer image has been successfully removed', errorMessage: 'An error occurred while removing the layer text', }, text: { question: 'Are you sure you want to remove the text?', successMessage: 'The layer text has been successfully removed', errorMessage: 'An error occurred while removing the layer text', }, }; export const LayersDrawerObjectsList = ({ layerId, isLayerVisible, isLayerActive, }: LayersDrawerObjectsListProps): JSX.Element | null => { const currentModelId = useAppSelector(mapModelIdSelector); const highestZIndex = useAppSelector(highestZIndexSelector); const lowestZIndex = useAppSelector(lowestZIndexSelector); const layer = useAppSelector(state => layerByIdSelector(state, layerId)); const mapEditToolsLayerImageObject = useAppSelector(mapEditToolsLayerObjectSelector); const [removeModalState, setRemoveModalState] = useState<undefined | 'text' | 'image'>(undefined); const [layerObjectToRemove, setLayerObjectToRemove] = useState<LayerImage | LayerText | null>( null, ); const dispatch = useAppDispatch(); const setBounds = useSetBounds(); const pointToProjection = usePointToProjection(); const { mapInstance } = useMapInstance(); const removeObject = (layerObject: LayerImage | LayerText): void => { setLayerObjectToRemove(layerObject); if ('glyph' in layerObject) { setRemoveModalState('image'); } else { setRemoveModalState('text'); } }; const rejectRemove = (): void => { setRemoveModalState(undefined); }; const confirmRemove = async (): Promise<void> => { if (!layerObjectToRemove || !removeModalState) { return; } try { if (removeModalState === 'text') { await dispatch( removeLayerText({ modelId: currentModelId, layerId: layerObjectToRemove.layer, textId: layerObjectToRemove.id, }), ).unwrap(); dispatch( layerDeleteText({ modelId: currentModelId, layerId: layerObjectToRemove.layer, textId: layerObjectToRemove.id, }), ); } else { await dispatch( removeLayerImage({ modelId: currentModelId, layerId: layerObjectToRemove.layer, imageId: layerObjectToRemove.id, }), ).unwrap(); dispatch( layerDeleteImage({ modelId: currentModelId, layerId: layerObjectToRemove.layer, imageId: layerObjectToRemove.id, }), ); } removeElementFromLayer({ mapInstance, layerId: layerObjectToRemove.layer, featureId: layerObjectToRemove.id, }); showToast({ type: 'success', message: removeObjectConfig[removeModalState].successMessage, }); setRemoveModalState(undefined); } catch (error) { const typedError = error as SerializedError; showToast({ type: 'error', message: typedError.message || removeObjectConfig[removeModalState].errorMessage, }); } }; const updateImageZIndex = async ({ zIndex, layerImage, }: { zIndex: number; layerImage: LayerImage; }): Promise<void> => { const newLayerImage = await dispatch( updateLayerImageObject({ modelId: currentModelId, layerId: layerImage.layer, ...layerImage, z: zIndex, }), ).unwrap(); if (newLayerImage) { dispatch( layerUpdateImage({ modelId: currentModelId, layerId: newLayerImage.layer, layerImage: newLayerImage, }), ); dispatch(mapEditToolsSetLayerObject(newLayerImage)); updateElement(mapInstance, newLayerImage.layer, newLayerImage); } }; const bringImageToFront = async (layerImage: LayerImage): Promise<void> => { await updateImageZIndex({ zIndex: highestZIndex + 1, layerImage }); }; const bringImageToBack = async (layerImage: LayerImage): Promise<void> => { await updateImageZIndex({ zIndex: lowestZIndex - 1, layerImage }); }; const updateTextZIndex = async ({ zIndex, layerText, }: { zIndex: number; layerText: LayerText; }): Promise<void> => { const newLayerText = await dispatch( updateLayerText({ modelId: currentModelId, layerId: layerText.layer, ...layerText, z: zIndex, }), ).unwrap(); if (newLayerText) { dispatch( layerUpdateText({ modelId: currentModelId, layerId: newLayerText.layer, layerText: newLayerText, }), ); dispatch(mapEditToolsSetLayerObject(newLayerText)); updateElement(mapInstance, newLayerText.layer, newLayerText); } }; const bringTextToFront = async (layerText: LayerText): Promise<void> => { await updateTextZIndex({ zIndex: highestZIndex + 1, layerText }); }; const bringTextToBack = async (layerText: LayerText): Promise<void> => { await updateTextZIndex({ zIndex: lowestZIndex - 1, layerText }); }; const centerObject = (layerObject: LayerImage | LayerText): void => { if (mapEditToolsLayerImageObject && mapEditToolsLayerImageObject.id === layerObject.id) { const point1 = pointToProjection({ x: layerObject.x, y: layerObject.y }); const point2 = pointToProjection({ x: layerObject.x + layerObject.width, y: layerObject.y + layerObject.height, }); setBounds([point1, point2] as Coordinate[]); } }; const editImage = (): void => { dispatch(openLayerImageObjectEditFactoryModal()); }; const editText = (): void => { dispatch(openLayerTextEditFactoryModal()); }; if (!layer) { return null; } return ( <div className={`${isLayerVisible ? 'opacity-100' : 'opacity-40'} flex flex-col gap-1 ps-3`}> <QuestionModal isOpen={Boolean(removeModalState)} onClose={rejectRemove} onConfirm={confirmRemove} question={ removeModalState ? removeObjectConfig[removeModalState]?.question : 'Are you sure you want to remove the object' } /> {Object.values(layer.texts).map(layerText => ( <LayersDrawerTextItem layerText={layerText} key={layerText.id} bringToFront={() => bringTextToFront(layerText)} bringToBack={() => bringTextToBack(layerText)} removeObject={() => removeObject(layerText)} centerObject={() => centerObject(layerText)} editObject={() => editText()} isLayerVisible={isLayerVisible} isLayerActive={isLayerActive} /> ))} {Object.values(layer.images).map(layerImage => ( <LayersDrawerImageItem layerImage={layerImage} key={layerImage.id} bringToFront={() => bringImageToFront(layerImage)} bringToBack={() => bringImageToBack(layerImage)} removeObject={() => removeObject(layerImage)} centerObject={() => centerObject(layerImage)} editObject={() => editImage()} isLayerVisible={isLayerVisible} isLayerActive={isLayerActive} /> ))} </div> ); };