Fixed camera mounting issue. Separated media cache modal. Starting working on deleting images after upload. IO-397 IO-398

This commit is contained in:
Patrick Fic
2020-11-17 17:19:17 -08:00
parent 8fc357a6d9
commit ccb42548d0
5 changed files with 175 additions and 75 deletions

View File

@@ -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 (
<Modal
animationType="slide"
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
visible={previewVisible}
transparent={true}
>
<ImageViewer
onCancel={() => setPreviewVisible(false)}
index={imgIndex}
onChange={(index) => setImgIndex(index)}
style={{ display: "flex" }}
renderFooter={(index) => (
<View
style={{
marginleft: "auto",
backgroundColor: "tomato",
}}
>
<Text>{index} This is the thing.</Text>
<Button
onPress={() => {
removePhotos([photos[index].id]);
}}
>
<Text>Delete</Text>
</Button>
</View>
)}
enableSwipeDown
enablePreload
imageUrls={photos.map((p) => {
return { url: p.uri };
})}
/>
</Modal>
);
}
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);

View File

@@ -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 <View />;
}

View File

@@ -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 (
<SafeAreaView style={styles.container}>
<FlatList
data={photos}
style={{ flex: 1 }}
style={{ flex: 5 }}
contentContainerStyle={styles.listContentContainer}
keyExtractor={(item) => item.id}
numColumns={5}
@@ -52,6 +65,12 @@ export function ScreenMediaCache({ photos, removeAllPhotos, uploadAllphotos }) {
)
}
/>
<FlatList
data={imagesInDir}
style={{ flex: 1, backgroundColor: "tomato" }}
keyExtractor={(item) => item.id}
renderItem={(object) => <Text>{object.item}</Text>}
/>
<Text>{`${photos.length} Photos`}</Text>
<View style={styles.actions}>
<Button onPress={() => removeAllPhotos()}>
@@ -61,35 +80,12 @@ export function ScreenMediaCache({ photos, removeAllPhotos, uploadAllphotos }) {
<NBText>Upload all</NBText>
</Button>
</View>
<Modal
animationType="slide"
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
visible={previewVisible}
transparent={true}
>
<ImageViewer
onCancel={() => setPreviewVisible(false)}
index={imgIndex}
onChange={(index) => setImgIndex(index)}
style={{ display: "flex" }}
renderFooter={(index) => (
<View
style={{
backgroundColor: "tomato",
}}
>
<Text>{index} This is the thing.</Text>
</View>
)}
enableSwipeDown
enablePreload
imageUrls={photos.map((p) => {
return { url: p.uri };
})}
/>
</Modal>
<MediaCacheOverlay
imgIndex={imgIndex}
setImgIndex={setImgIndex}
previewVisible={previewVisible}
setPreviewVisible={setPreviewVisible}
/>
</SafeAreaView>
);
}

View File

@@ -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)]);
}

View File

@@ -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 };
};