From ccb42548d0b7071930bef2475b2c3578e16310aa Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 17 Nov 2020 17:19:17 -0800
Subject: [PATCH] Fixed camera mounting issue. Separated media cache modal.
Starting working on deleting images after upload. IO-397 IO-398
---
.../media-cache-overlay.component.jsx | 85 +++++++++++++++++++
.../screen-camera/screen-camera.component.jsx | 17 +++-
.../screen-media-cache.component.jsx | 64 +++++++-------
redux/photos/photos.sagas.js | 54 ++++++++----
util/document-upload.utility.js | 30 ++-----
5 files changed, 175 insertions(+), 75 deletions(-)
create mode 100644 components/media-cache-overlay/media-cache-overlay.component.jsx
diff --git a/components/media-cache-overlay/media-cache-overlay.component.jsx b/components/media-cache-overlay/media-cache-overlay.component.jsx
new file mode 100644
index 0000000..65f6c2f
--- /dev/null
+++ b/components/media-cache-overlay/media-cache-overlay.component.jsx
@@ -0,0 +1,85 @@
+import { Button, View } from "native-base";
+import React from "react";
+import { Alert, Modal, StyleSheet, Text } from "react-native";
+import ImageViewer from "react-native-image-zoom-viewer";
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
+import { removePhotos } from "../../redux/photos/photos.actions";
+import { selectPhotos } from "../../redux/photos/photos.selectors";
+
+const mapStateToProps = createStructuredSelector({
+ photos: selectPhotos,
+});
+const mapDispatchToProps = (dispatch) => ({
+ removePhotos: (ids) => dispatch(removePhotos(ids)),
+});
+
+export function MediaCacheOverlay({
+ photos,
+ removePhotos,
+ previewVisible,
+ setPreviewVisible,
+ imgIndex,
+ setImgIndex,
+}) {
+ return (
+ {
+ Alert.alert("Modal has been closed.");
+ }}
+ visible={previewVisible}
+ transparent={true}
+ >
+ setPreviewVisible(false)}
+ index={imgIndex}
+ onChange={(index) => setImgIndex(index)}
+ style={{ display: "flex" }}
+ renderFooter={(index) => (
+
+ {index} This is the thing.
+
+
+ )}
+ enableSwipeDown
+ enablePreload
+ imageUrls={photos.map((p) => {
+ return { url: p.uri };
+ })}
+ />
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ actions: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-evenly",
+ },
+ listContentContainer: {
+ flex: 1,
+ },
+ thumbnail: {
+ width: 10,
+ height: 10,
+ backgroundColor: "tomato",
+ },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(MediaCacheOverlay);
diff --git a/components/screen-camera/screen-camera.component.jsx b/components/screen-camera/screen-camera.component.jsx
index eacc253..f755412 100644
--- a/components/screen-camera/screen-camera.component.jsx
+++ b/components/screen-camera/screen-camera.component.jsx
@@ -1,5 +1,5 @@
import { Ionicons } from "@expo/vector-icons";
-import { useNavigation } from "@react-navigation/native";
+import { useFocusEffect, useNavigation } from "@react-navigation/native";
import { Camera } from "expo-camera";
import * as FileSystem from "expo-file-system";
import React, { useEffect, useRef, useState } from "react";
@@ -28,9 +28,22 @@ export function ScreenCamera({ cameraJobId, cameraJob, addPhoto }) {
flashMode: Camera.Constants.FlashMode.off,
capturing: null,
cameraType: Camera.Constants.Type.back,
+ tabHasFocus: null,
});
const cameraRef = useRef(null);
+ useFocusEffect(
+ React.useCallback(() => {
+ // Do something when the screen is focused
+ setState({ ...state, tabHasFocus: true });
+ return () => {
+ // Do something when the screen is unfocused
+ // Useful for cleanup functions
+ setState({ ...state, tabHasFocus: false });
+ };
+ }, [])
+ );
+
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
@@ -101,7 +114,7 @@ export function ScreenCamera({ cameraJobId, cameraJob, addPhoto }) {
}
};
- if (hasPermission === null) {
+ if (hasPermission === null || !state.tabHasFocus) {
return ;
}
diff --git a/components/screen-media-cache/screen-media-cache.component.jsx b/components/screen-media-cache/screen-media-cache.component.jsx
index 54d058a..f114904 100644
--- a/components/screen-media-cache/screen-media-cache.component.jsx
+++ b/components/screen-media-cache/screen-media-cache.component.jsx
@@ -1,15 +1,13 @@
+import * as FileSystem from "expo-file-system";
import { Button, Text as NBText, Thumbnail, View } from "native-base";
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import {
- Alert,
FlatList,
- Modal,
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
} from "react-native";
-import ImageViewer from "react-native-image-zoom-viewer";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
@@ -17,6 +15,7 @@ import {
uploadAllPhotos,
} from "../../redux/photos/photos.actions";
import { selectPhotos } from "../../redux/photos/photos.selectors";
+import MediaCacheOverlay from "../media-cache-overlay/media-cache-overlay.component";
const mapStateToProps = createStructuredSelector({
photos: selectPhotos,
@@ -29,11 +28,25 @@ const mapDispatchToProps = (dispatch) => ({
export function ScreenMediaCache({ photos, removeAllPhotos, uploadAllphotos }) {
const [previewVisible, setPreviewVisible] = useState(false);
const [imgIndex, setImgIndex] = useState(0);
+ const [imagesInDir, setImagesInDir] = useState([]);
+
+ useEffect(() => {
+ const check = async () => {
+ setImagesInDir(
+ await FileSystem.readDirectoryAsync(
+ FileSystem.documentDirectory + "photos"
+ )
+ );
+ };
+ photos.length;
+ check();
+ }, [photos]);
+
return (
item.id}
numColumns={5}
@@ -52,6 +65,12 @@ export function ScreenMediaCache({ photos, removeAllPhotos, uploadAllphotos }) {
)
}
/>
+ item.id}
+ renderItem={(object) => {object.item}}
+ />
{`${photos.length} Photos`}
- {
- Alert.alert("Modal has been closed.");
- }}
- visible={previewVisible}
- transparent={true}
- >
- setPreviewVisible(false)}
- index={imgIndex}
- onChange={(index) => setImgIndex(index)}
- style={{ display: "flex" }}
- renderFooter={(index) => (
-
- {index} This is the thing.
-
- )}
- enableSwipeDown
- enablePreload
- imageUrls={photos.map((p) => {
- return { url: p.uri };
- })}
- />
-
+
);
}
diff --git a/redux/photos/photos.sagas.js b/redux/photos/photos.sagas.js
index 6a53ff3..bf3ad3d 100644
--- a/redux/photos/photos.sagas.js
+++ b/redux/photos/photos.sagas.js
@@ -3,6 +3,25 @@ import { all, call, select, takeLatest } from "redux-saga/effects";
import { handleUpload } from "../../util/document-upload.utility";
import PhotosActionTypes from "./photos.types";
+export function* onRemovePhotos() {
+ yield takeLatest(PhotosActionTypes.REMOVE_PHOTOS, removePhotosAction);
+}
+export function* removePhotosAction({ payload: photoIdsToRemove }) {
+ 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);
}
@@ -30,6 +49,7 @@ export function* onUploadAllPhotos() {
uploadAllPhotosAction
);
}
+
export function* uploadAllPhotosAction() {
try {
const photos = yield select((state) => state.photos.photos);
@@ -38,41 +58,39 @@ export function* uploadAllPhotosAction() {
const actions = [];
photos.forEach(async (p) =>
actions.push(
- handleUpload(
+ await handleUpload(
{
file: await (await fetch(p.uri)).blob(),
-
- onError: (props) => {
- console.log("Error Callback", props);
- },
- onProgress: (props) => {
- console.log("Progress Calback", props);
- },
- onSuccess: (props) => {
- console.log("Success Calback", props);
- },
+ onError: handleOnError,
+ onProgress: handleOnProgress,
+ onSuccess: handleOnSuccess,
},
{
bodyshop: bodyshop,
jobId: p.jobId,
uploaded_by: user.currentUser.email,
- callback: (props) => {
- console.log("Context Callback", props);
- },
- photo: {
- ...p,
- name: p.uri.substring(p.uri.lastIndexOf("/") + 1),
- },
+ photo: p,
}
)
)
);
yield Promise.all(actions);
+ console.log("function*uploadAllPhotosAction -> actions", actions);
} catch (error) {
console.log("Saga Error: onRemoveAllPhotos", 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)]);
}
diff --git a/util/document-upload.utility.js b/util/document-upload.utility.js
index 75bfaa0..a7f8da6 100644
--- a/util/document-upload.utility.js
+++ b/util/document-upload.utility.js
@@ -9,7 +9,7 @@ import { axiosAuthInterceptorId } from "./CleanAxios";
var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
-export const handleUpload = (ev, context) => {
+export const handleUpload = async (ev, context) => {
const { onError, onSuccess, onProgress } = ev;
const { bodyshop, jobId } = context;
@@ -18,7 +18,7 @@ export const handleUpload = (ev, context) => {
""
)}`;
- uploadToCloudinary(
+ return uploadToCloudinary(
key,
ev.file.type,
ev.file,
@@ -70,18 +70,14 @@ export const uploadToCloudinary = async (
);
} catch (error) {
console.log("ERROR GETTING SIGNED URL", error);
- return;
+ return { success: false, error: error };
}
if (signedURLResponse.status !== 200) {
console.log("Error Getting Signed URL", signedURLResponse.statusText);
if (!!onError) onError(signedURLResponse.statusText);
- // notification["error"]({
- // message: i18n.t("documents.errors.getpresignurl", {
- // message: signedURLResponse.statusText,
- // }),
- // });
- return;
+
+ return { success: false, error: signedURLResponse.statusText };
}
//Build request to end to cloudinary.
@@ -94,11 +90,6 @@ export const uploadToCloudinary = async (
};
const formData = new FormData();
- console.log("Sending!", {
- uri: photo.uri,
- type: fileType,
- name: file.data.name,
- });
formData.append("file", {
uri: photo.uri,
type: fileType,
@@ -126,6 +117,7 @@ export const uploadToCloudinary = async (
console.log("Cloudinary Upload Response", cloudinaryUploadResponse.data);
} catch (error) {
console.log("CLOUDINARY error", error, cloudinaryUploadResponse);
+ return { success: false, error: error };
}
if (cloudinaryUploadResponse.status !== 200) {
@@ -135,12 +127,7 @@ export const uploadToCloudinary = async (
cloudinaryUploadResponse
);
if (!!onError) onError(cloudinaryUploadResponse.statusText);
- // notification["error"]({
- // message: i18n.t("documents.errors.insert", {
- // message: cloudinaryUploadResponse.statusText,
- // }),
- // });
- return;
+ return { success: false, error: cloudinaryUploadResponse.statusText };
}
//Insert the document with the matching key.
@@ -179,6 +166,7 @@ export const uploadToCloudinary = async (
// message: JSON.stringify(JSON.stringify(documentInsert.errors)),
// }),
// });
- return;
+ return { success: false, error: JSON.stringify(documentInsert.errors) };
}
+ return { success: true };
};