From 620b5135d10d1c8bbdddaec172ab928d89e1de83 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 8 Oct 2025 16:20:23 -0700 Subject: [PATCH] Add photos saga work. --- components/jobs-list/job-list-item.jsx | 13 ++- redux/photos/photos.actions.js | 36 ++----- redux/photos/photos.reducer.js | 32 +----- redux/photos/photos.sagas.js | 135 +++++++++---------------- redux/photos/photos.types.js | 12 +-- 5 files changed, 70 insertions(+), 158 deletions(-) diff --git a/components/jobs-list/job-list-item.jsx b/components/jobs-list/job-list-item.jsx index f4adfec..1e17f79 100644 --- a/components/jobs-list/job-list-item.jsx +++ b/components/jobs-list/job-list-item.jsx @@ -4,15 +4,16 @@ import { Button, List, Text, useTheme } from "react-native-paper"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../firebase/firebase.analytics"; -import { setCameraJob, setCameraJobId } from "../../redux/app/app.actions"; +import { setCameraJobId } from "../../redux/app/app.actions"; +import { openImagePicker } from "../../redux/photos/photos.actions"; const mapStateToProps = createStructuredSelector({}); const mapDispatchToProps = (dispatch) => ({ setCameraJobId: (id) => dispatch(setCameraJobId(id)), - setCameraJob: (job) => dispatch(setCameraJob(job)), + openImagePicker: (id) => dispatch(openImagePicker(id)), }); -export function JobListItem({ setCameraJob, setCameraJobId, item }) { +export function JobListItem({ openImagePicker, item }) { const { t } = useTranslation(); const router = useRouter(); const theme = useTheme(); @@ -26,11 +27,13 @@ export function JobListItem({ setCameraJob, setCameraJobId, item }) { }); }; - const handleUpload = () => {}; + const handleUpload = () => { + openImagePicker(item.id); + }; return ( ({ - type: PhotosActionTypes.ADD_PHOTO, - payload: photo, +export const openImagePicker = (jobid) => ({ + type: PhotosActionTypes.OPEN_IMAGE_PICKER, + payload: jobid }); -export const removePhotos = (photoIds) => ({ - type: PhotosActionTypes.REMOVE_PHOTOS, - payload: photoIds, -}); - -export const removeAllPhotos = () => ({ - type: PhotosActionTypes.REMOVE_ALL_PHOTOS, -}); - -export const uploadAllPhotos = () => ({ - type: PhotosActionTypes.UPLOAD_ALL_PHOTOS_START, -}); - -export const uploadSelectedPhotos = (photoIds) => ({ - type: PhotosActionTypes.UPLOAD_SELECTED_PHOTOS_START, - payload: photoIds, -}); - -export const uploadPhotosSuccess = () => ({ - type: PhotosActionTypes.UPLOAD_PHOTO_SUCCESS, -}); - -export const uploadPhotosFailure = (error) => ({ - type: PhotosActionTypes.UPLOAD_PHOTO_FAILURE, - payload: error, -}); +export const mediaUploadStart = (imagePickerResult) => ({ + type: PhotosActionTypes.MEDIA_UPLOAD_START, + payload: imagePickerResult, +}) \ No newline at end of file diff --git a/redux/photos/photos.reducer.js b/redux/photos/photos.reducer.js index 647950a..19610e8 100644 --- a/redux/photos/photos.reducer.js +++ b/redux/photos/photos.reducer.js @@ -8,40 +8,14 @@ const INITIAL_STATE = { const photosReducer = (state = INITIAL_STATE, action) => { switch (action.type) { - case PhotosActionTypes.ADD_PHOTO: - return { - ...state, - photos: [...state.photos, action.payload], - }; - case PhotosActionTypes.REMOVE_ALL_PHOTOS: - return { - ...state, - uploadInProgress: false, - photos: [], - }; - case PhotosActionTypes.REMOVE_PHOTOS: - return { - ...state, - photos: state.photos.filter((p) => !action.payload.includes(p.id)), - }; - case PhotosActionTypes.UPLOAD_PHOTO_FAILURE: - return { - ...state, - uploadInProgress: false, - uploadError: action.payload, - }; - case PhotosActionTypes.UPLOAD_PHOTO_SUCCESS: - return { - ...state, - uploadInProgress: false, - }; - case PhotosActionTypes.UPLOAD_ALL_PHOTOS_START: - case PhotosActionTypes.UPLOAD_SELECTED_PHOTOS_START: + case PhotosActionTypes.MEDIA_UPLOAD_START: return { ...state, + photos: action.payload, uploadInProgress: true, uploadError: null, }; + default: return state; } diff --git a/redux/photos/photos.sagas.js b/redux/photos/photos.sagas.js index 09e94ce..5e2195c 100644 --- a/redux/photos/photos.sagas.js +++ b/redux/photos/photos.sagas.js @@ -1,106 +1,65 @@ -import * as FileSystem from "expo-file-system"; +import Constants from "expo-constants"; +import * as ImagePicker from "expo-image-picker"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { handleUpload } from "../../util/document-upload.utility"; -import { - uploadPhotosFailure, - uploadPhotosSuccess, - removeAllPhotos, -} from "./photos.actions"; +import { selectBodyshop } from "../user/user.selectors"; +import { mediaUploadStart } from "./photos.actions"; import PhotosActionTypes from "./photos.types"; -export function* onRemovePhotos() { - yield takeLatest(PhotosActionTypes.REMOVE_PHOTOS, removePhotosAction); +export function* onOpenImagePicker() { + yield takeLatest(PhotosActionTypes.OPEN_IMAGE_PICKER, openImagePickerAction); } -export function* removePhotosAction({ payload: photoIdsToRemove }) { +export function* openImagePickerAction(jobid) { try { - const photos = yield select((state) => state.photos.photos); - const fps = photos - .filter((p) => !photoIdsToRemove.includes(p.id)) - .map((p) => p.uri); - - const all = []; - fps.forEach((f) => all.push(FileSystem.deleteAsync(f))); - - yield Promise.all(all); - } catch (error) { - console.log("Saga Error: removePhotos", error); - } -} - -export function* onRemoveAllPhotos() { - yield takeLatest(PhotosActionTypes.REMOVE_ALL_PHOTOS, removeAllPhotosAction); -} -export function* removeAllPhotosAction() { - try { - //Physically delete all photosSagas. - const fps = (yield FileSystem.readDirectoryAsync( - FileSystem.documentDirectory + "photos" - )).map((f) => { - return FileSystem.documentDirectory + "photos/" + f; + if (Constants.platform.ios) { + const cameraRollStatus = + yield ImagePicker.requestMediaLibraryPermissionsAsync(); + const cameraStatus = yield ImagePicker.requestCameraPermissionsAsync(); + if ( + cameraRollStatus.status !== "granted" || + cameraStatus.status !== "granted" + ) { + alert("Sorry, we need these permissions to make this work!"); + return; + } + } + let result = yield ImagePicker.launchImageLibraryAsync({ + mediaTypes: ["images", "videos"], + aspect: [4, 3], + quality: 1, + allowsMultipleSelection: true, }); - const all = []; - fps.forEach((f) => all.push(FileSystem.deleteAsync(f))); - yield Promise.all(all); - - console.log("All photos deleted."); + if (!(result.canceled)) { + yield put(mediaUploadStart(result.assets)); + } } catch (error) { - console.log("Saga Error: onRemoveAllPhotos", error); + console.log("Saga Error: open Picker", error); } } - -export function* onUploadAllPhotos() { - yield takeLatest( - PhotosActionTypes.UPLOAD_ALL_PHOTOS_START, - uploadAllPhotosAction - ); +export function* onMediaUploadStart() { + yield takeLatest(PhotosActionTypes.MEDIA_UPLOAD_START, mediaUploadStartAction); } - -export function* uploadAllPhotosAction() { +export function* mediaUploadStartAction(photos) { try { - const photos = yield select((state) => state.photos.photos); - const bodyshop = yield select((state) => state.user.bodyshop); - const user = yield select((state) => state.user); - const actions = []; - photos.forEach(function (p) { - actions.push( - call( - handleUpload, - ...[ - { - uri: p.uri, - onError: handleOnError, - onProgress: handleOnProgress, - onSuccess: handleOnSuccess, - }, - { - bodyshop: bodyshop, - jobId: p.jobId, - uploaded_by: user.currentUser.email, - photo: p, - }, - ] - ) - ); - }); - yield all(actions); - yield put(removeAllPhotos()); - yield put(uploadPhotosSuccess()); + console.log("Got to the Photo Saga.", photos); + + console.log("upload", photos) + + //get bodyshop state + const bodyshop = yield select(selectBodyshop); + if (bodyshop.uselocalmediaserver) { + //upload to LMS + } else { + //Upload to img proxy + } + + } catch (error) { - console.log("Saga Error: uploadAllPhotosAction", error); - yield put(uploadPhotosFailure(error)); + console.log("Saga Error: open upload", error); } } -function handleOnError(...props) { - console.log("HandleOnError", props); -} -function handleOnProgress(...props) { - console.log("HandleOnProgress", props); -} -function handleOnSuccess(...props) { - console.log("handleOnSuccess", props); -} + export function* photosSagas() { - yield all([call(onRemoveAllPhotos), call(onUploadAllPhotos)]); + yield all([call(onOpenImagePicker), call(onMediaUploadStart)]); } diff --git a/redux/photos/photos.types.js b/redux/photos/photos.types.js index 6b32087..8361795 100644 --- a/redux/photos/photos.types.js +++ b/redux/photos/photos.types.js @@ -1,10 +1,8 @@ const PhotosActionTypes = { - ADD_PHOTO: "ADD_PHOTO", - REMOVE_PHOTOS: "REMOVE_PHOTOS", - REMOVE_ALL_PHOTOS: "REMOVE_ALL_PHOTOS", - UPLOAD_SELECTED_PHOTOS_START: "UPLOAD_SELECTED_PHOTOS_START", - UPLOAD_ALL_PHOTOS_START: "UPLOAD_ALL_PHOTOS_START", - UPLOAD_PHOTO_SUCCESS: "UPLOAD_PHOTO_SUCCESS", - UPLOAD_PHOTO_FAILURE: "UPLOAD_PHOTO_FAILURE", + OPEN_IMAGE_PICKER: "OPEN_IMAGE_PICKER", + MEDIA_UPLOAD_START: "MEDIA_UPLOAD_START", + MEDIUA_UPLOAD_SUCCESS: "MEDIA_UPLOAD_SUCCESS", + MEDIA_UPLOAD_FAILURE: "MEDIA_UPLOAD_FAILURE", + MEDIA_UPLOAD_PROGRESS_UPDATE: "MEDIA_UPLOAD_PROGRESS_UPDATE", }; export default PhotosActionTypes;