diff --git a/src/features/cameras/DeleteCameraForm.jsx b/src/features/cameras/DeleteCameraForm.jsx index 7cc3e3ba..a980092d 100644 --- a/src/features/cameras/DeleteCameraForm.jsx +++ b/src/features/cameras/DeleteCameraForm.jsx @@ -8,8 +8,13 @@ import { FormWrapper, ButtonRow, HelperText } from '../../components/Form.jsx'; import { deleteCamera, fetchTask, selectDeleteCameraLoading } from '../tasks/tasksSlice.js'; import { SimpleSpinner, SpinnerOverlay } from '../../components/Spinner.jsx'; import { selectSelectedCamera } from '../projects/projectsSlice.js'; +import { + fetchCameraImageCount, + selectCameraImageCount, + selectCameraImageCountLoading, +} from '../cameras/wirelessCamerasSlice.js'; -const CameraId = styled('span', { +const BoldText = styled('span', { fontWeight: '$5', }); @@ -21,12 +26,19 @@ const DeleteCameraForm = ({ handleClose }) => { const [queuedForClose, setQueuedForClose] = useState(false); const deleteCameraLoading = useSelector(selectDeleteCameraLoading); const selectedCamera = useSelector(selectSelectedCamera); + const imageCount = useSelector(selectCameraImageCount); + const imageCountLoading = useSelector(selectCameraImageCountLoading); const dispatch = useDispatch(); - // TODO: extract into hook? useEffect(() => { - if (queuedForClose) handleClose(); - }, [queuedForClose, handleClose]); + if (imageCount === null && selectedCamera !== null && !imageCountLoading) { + dispatch(fetchCameraImageCount({ cameraId: selectedCamera })); + } + }, [imageCount, selectedCamera, dispatch]); + + useEffect(() => { + if (queuedForClose && !deleteCameraLoading.isLoading) handleClose(); + }, [deleteCameraLoading, queuedForClose, handleClose]); const handleDeleteCameraSubmit = (formVals) => { dispatch(deleteCamera(formVals)); @@ -41,9 +53,11 @@ const DeleteCameraForm = ({ handleClose }) => { } }, [deleteCameraLoading, dispatch]); + const imagesText = `${imageCount} ${imageCount === 1 ? ' image' : ' images'}`; + return (
- {deleteCameraLoading.isLoading && ( + {(deleteCameraLoading.isLoading || imageCountLoading) && ( @@ -59,9 +73,10 @@ const DeleteCameraForm = ({ handleClose }) => { {() => (
- Are you sure you'd like to delete Camera {selectedCamera}? + Are you sure you'd like to delete Camera {selectedCamera}? This will remove the camera from the project, remove all deployments associated with - it, and delete all images. This action cannot be undone. + it, and {imagesText} will be deleted.{' '} + This action cannot be undone. diff --git a/src/features/cameras/wirelessCamerasSlice.js b/src/features/cameras/wirelessCamerasSlice.js index 32c6a80a..36b87962 100644 --- a/src/features/cameras/wirelessCamerasSlice.js +++ b/src/features/cameras/wirelessCamerasSlice.js @@ -5,9 +5,14 @@ import { setSelectedProjAndView } from '../projects/projectsSlice'; const initialState = { wirelessCameras: [], + cameraImageCount: { + isLoading: false, + currentCameraId: null, + count: null, + }, loadingState: { isLoading: false, - operation: null, /* 'fetching', 'updating', 'deleting' */ + operation: null /* 'fetching', 'updating', 'deleting' */, errors: null, noneFound: false, }, @@ -17,7 +22,6 @@ export const wirelessCamerasSlice = createSlice({ name: 'wirelessCameras', initialState, reducers: { - getWirelessCamerasStart: (state) => { state.loadingState.isLoading = true; state.loadingState.operation = 'fetching'; @@ -35,7 +39,7 @@ export const wirelessCamerasSlice = createSlice({ isLoading: false, operation: null, errors: null, - noneFound: (payload.length === 0), + noneFound: payload.length === 0, }; }, @@ -58,13 +62,13 @@ export const wirelessCamerasSlice = createSlice({ isLoading: false, operation: null, errors: null, - noneFound: (payload.wirelessCameras.length === 0), + noneFound: payload.wirelessCameras.length === 0, }; // TODO: make the cameras update update more surgical? - // i.e. ONLY return the new/updated Camera source record and merge it - // into existing cameras (like we do with Views), and only return the - // new cameraConfig & merge that with Project.cameras array. - // Advantages: don't have to do getCameras() on backend before returning, + // i.e. ONLY return the new/updated Camera source record and merge it + // into existing cameras (like we do with Views), and only return the + // new cameraConfig & merge that with Project.cameras array. + // Advantages: don't have to do getCameras() on backend before returning, // less data in payload }, @@ -87,7 +91,7 @@ export const wirelessCamerasSlice = createSlice({ isLoading: false, operation: null, errors: null, - noneFound: (payload.wirelessCameras.length === 0), + noneFound: payload.wirelessCameras.length === 0, }; }, @@ -95,28 +99,45 @@ export const wirelessCamerasSlice = createSlice({ const index = payload; state.loadingState.errors.splice(index, 1); }, - + + /* Fetch image count for specific camera, so as to not clash with imageCount in imagesSlice */ + + cameraImageCountStart: (state, { payload }) => { + state.cameraImageCount.isLoading = true; + state.cameraImageCount.currentCameraId = payload.cameraId; + state.cameraImageCount.count = null; + }, + + cameraImageCountSuccess(state, { payload }) { + state.cameraImageCount.isLoading = false; + state.cameraImageCount.currentCameraId = null; + state.cameraImageCount.count = payload.imagesCount.count; + }, + + cameraImageCountError(state) { + state.cameraImageCount.isLoading = false; + state.cameraImageCount.currentCameraId = null; + state.cameraImageCount.count = null; + }, }, extraReducers: (builder) => { - builder - .addCase(setSelectedProjAndView, (state, { payload }) => { - if (payload.newProjSelected) { - state.wirelessCameras = []; - state.loadingState = { - isLoading: false, - operation: null, - errors: null, - noneFound: null, - }; - } - }) + builder.addCase(setSelectedProjAndView, (state, { payload }) => { + if (payload.newProjSelected) { + state.wirelessCameras = []; + state.loadingState = { + isLoading: false, + operation: null, + errors: null, + noneFound: null, + }; + } + }); }, }); // export actions from slice export const { - getWirelessCamerasStart, getWirelessCamerasFailure, getWirelessCamerasSuccess, @@ -131,6 +152,9 @@ export const { dismissWirelessCamerasError, + cameraImageCountStart, + cameraImageCountSuccess, + cameraImageCountError, } = wirelessCamerasSlice.actions; // fetchWirelessCameras thunk @@ -169,9 +193,8 @@ export const registerCamera = (payload) => { request: 'registerCamera', input: payload, }); - dispatch(registerCameraSuccess(res.registerCamera)) + dispatch(registerCameraSuccess(res.registerCamera)); } - } catch (err) { console.log(`error(s) attempting to register camera: `, err); dispatch(registerCameraFailure(err)); @@ -179,7 +202,7 @@ export const registerCamera = (payload) => { }; }; -// unregisger camera thunk +// unregister camera thunk export const unregisterCamera = (payload) => async (dispatch, getState) => { try { dispatch(unregisterCameraStart()); @@ -192,7 +215,7 @@ export const unregisterCamera = (payload) => async (dispatch, getState) => { const res = await call({ projId: selectedProj._id, request: 'unregisterCamera', - input: payload + input: payload, }); dispatch(unregisterCameraSuccess(res.unregisterCamera)); } @@ -201,10 +224,35 @@ export const unregisterCamera = (payload) => async (dispatch, getState) => { } }; +// fetchCameraImageCount thunk +export const fetchCameraImageCount = (payload) => async (dispatch, getState) => { + try { + dispatch(cameraImageCountStart(payload)); + const currentUser = await Auth.currentAuthenticatedUser(); + const token = currentUser.getSignInUserSession().getIdToken().getJwtToken(); + const projects = getState().projects.projects; + const selectedProj = projects.find((proj) => proj.selected); + + if (token && selectedProj) { + const res = await call({ + projId: selectedProj._id, + request: 'getImagesCount', + input: { filters: { cameras: [payload.cameraId] } }, + }); + dispatch(cameraImageCountSuccess(res)); + } + } catch (err) { + console.log(`error(s) attempting to fetch image count for camera ${payload.cameraId}: `, err); + dispatch(cameraImageCountError(err)); + } +}; // Selectors -export const selectWirelessCameras = state => state.wirelessCameras.wirelessCameras; -export const selectWirelessCamerasLoading = state => state.wirelessCameras.loadingState; -export const selectWirelessCamerasErrors = state => state.wirelessCameras.loadingState.errors; +export const selectWirelessCameras = (state) => state.wirelessCameras.wirelessCameras; +export const selectWirelessCamerasLoading = (state) => state.wirelessCameras.loadingState; +export const selectWirelessCamerasErrors = (state) => state.wirelessCameras.loadingState.errors; +export const selectCameraImageCount = (state) => state.wirelessCameras.cameraImageCount.count; +export const selectCameraImageCountLoading = (state) => + state.wirelessCameras.cameraImageCount.isLoading; export default wirelessCamerasSlice.reducer; diff --git a/src/features/images/DeleteImagesAlert.jsx b/src/features/images/DeleteImagesAlert.jsx index 2a3d3b91..a523a7a0 100644 --- a/src/features/images/DeleteImagesAlert.jsx +++ b/src/features/images/DeleteImagesAlert.jsx @@ -130,8 +130,14 @@ const DeleteImagesAlert = () => { (alertState.deleteImagesAlertByFilter && imageCountIsLoading.isLoading) || imagesLoading.isLoading; - const filterTitle = `Are you sure you'd like to delete ${imageCount === 1 ? 'this image' : `these ${imageCount && imageCount.toLocaleString()} images`}?`; - const selectionTitle = `Are you sure you'd like to delete ${selectedImages.length === 1 ? 'this image' : `these ${selectedImages && selectedImages.length.toLocaleString()} images`}?`; + const filterTitle = `Are you sure you'd like to delete ${ + imageCount === 1 ? 'this image' : `these ${imageCount && imageCount.toLocaleString()} images` + }?`; + const selectionTitle = `Are you sure you'd like to delete ${ + selectedImages.length === 1 + ? 'this image' + : `these ${selectedImages && selectedImages.length.toLocaleString()} images` + }?`; const filterText = (

diff --git a/src/features/tasks/tasksSlice.js b/src/features/tasks/tasksSlice.js index 72ff0df6..355c5b24 100644 --- a/src/features/tasks/tasksSlice.js +++ b/src/features/tasks/tasksSlice.js @@ -656,7 +656,7 @@ export const deleteCamera = (payload) => { input: payload, }); console.log('deleteCamera - res: ', res); - dispatch(updateDeleteCameraUpdate({ taskId: res.deleteCamera._id })); + dispatch(updateDeleteCameraUpdate({ taskId: res.deleteCameraConfig._id })); } } catch (err) { console.log('error attempting to delete camera: ', err);