IO-760 Delete pictures in bulk & other minor fixes

This commit is contained in:
Patrick Fic
2021-03-12 10:52:05 -07:00
parent 09cc798951
commit a5ca1b61a3
11 changed files with 137 additions and 130 deletions

2
App.js
View File

@@ -11,8 +11,6 @@ import { client } from "./graphql/client";
import { persistor, store } from "./redux/store"; import { persistor, store } from "./redux/store";
import "./translations/i18n"; import "./translations/i18n";
console.log("Environment Variables Set:", env);
Sentry.init({ Sentry.init({
dsn: dsn:
"https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.sentry.io/5558869", "https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.sentry.io/5558869",

View File

@@ -1,4 +1,4 @@
import React, { useState, useMemo } from "react"; import React, { useMemo, useState } from "react";
import { import {
FlatList, FlatList,
Image, Image,
@@ -32,6 +32,8 @@ export default function JobDocumentsComponent({ job, loading, refetch }) {
[job.documents] [job.documents]
); );
console.log("fullphotos", fullphotos);
return ( return (
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<FlatList <FlatList
@@ -40,6 +42,7 @@ export default function JobDocumentsComponent({ job, loading, refetch }) {
} }
data={job.documents} data={job.documents}
numColumns={4} numColumns={4}
style={{ flex: 1 }}
keyExtractor={(item) => item.id} keyExtractor={(item) => item.id}
renderItem={(object) => ( renderItem={(object) => (
<View <View

View File

@@ -1,15 +1,12 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import React, { useRef } from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Animated } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { Button, List, Title } from "react-native-paper"; import { Button, List, Title } from "react-native-paper";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { setCameraJob, setCameraJobId } from "../../redux/app/app.actions"; import { setCameraJob, setCameraJobId } from "../../redux/app/app.actions";
import styles from "../styles";
const mapStateToProps = createStructuredSelector({}); const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
@@ -20,35 +17,35 @@ const mapDispatchToProps = (dispatch) => ({
export function JobListItem({ setCameraJob, setCameraJobId, item }) { export function JobListItem({ setCameraJob, setCameraJobId, item }) {
const { t } = useTranslation(); const { t } = useTranslation();
const navigation = useNavigation(); const navigation = useNavigation();
const _swipeableRow = useRef(null); // const _swipeableRow = useRef(null);
const RenderRightAction = (progress, dragX) => { // const RenderRightAction = (progress, dragX) => {
const scale = dragX.interpolate({ // const scale = dragX.interpolate({
inputRange: [-100, 0], // inputRange: [-100, 0],
outputRange: [0.7, 0], // outputRange: [0.7, 0],
}); // });
return ( // return (
<TouchableOpacity // <TouchableOpacity
style={[styles.swipe_view, styles.swipe_view_blue]} // style={[styles.swipe_view, styles.swipe_view_blue]}
onPress={() => { // onPress={() => {
logImEXEvent("imexmobile_setcamerajobid_swipe"); // logImEXEvent("imexmobile_setcamerajobid_swipe");
setCameraJobId(item.id); // setCameraJobId(item.id);
setCameraJob(item); // setCameraJob(item);
navigation.navigate("MediaBrowserTab"); // navigation.navigate("MediaBrowserTab");
_swipeableRow.current.close(); // _swipeableRow.current.close();
}} // }}
> // >
<Animated.View // <Animated.View
style={{ // style={{
transform: [{ scale }], // transform: [{ scale }],
}} // }}
> // >
<Ionicons name="ios-camera" size={64} color="white" /> // <Ionicons name="ios-camera" size={64} color="white" />
</Animated.View> // </Animated.View>
</TouchableOpacity> // </TouchableOpacity>
); // );
}; // };
const onPress = () => { const onPress = () => {
logImEXEvent("imexmobile_view_job_detail"); logImEXEvent("imexmobile_view_job_detail");
@@ -59,35 +56,33 @@ export function JobListItem({ setCameraJob, setCameraJobId, item }) {
}; };
return ( return (
<TouchableOpacity onPress={onPress}> <List.Item
<List.Item onPress={onPress}
title={<Title>{item.ro_number || t("general.labels.na")}</Title>} title={<Title>{item.ro_number || t("general.labels.na")}</Title>}
description={`${item.ownr_fn || ""} ${item.ownr_ln || ""} ${ description={`${item.ownr_fn || ""} ${item.ownr_ln || ""} ${
item.ownr_co_nm || "" item.ownr_co_nm || ""
} - ${item.v_model_yr || ""} ${item.v_make_desc || ""} ${ } - ${item.v_model_yr || ""} ${item.v_make_desc || ""} ${
item.v_model_desc || "" item.v_model_desc || ""
}`} }`}
right={({ style }) => ( right={({ style }) => (
<Button <Button
style={style} style={[style, { alignSelf: "center" }]}
onPress={() => { onPress={() => {
logImEXEvent("imexmobile_setcamerajobid_swipe"); logImEXEvent("imexmobile_setcamerajobid_swipe");
setCameraJobId(item.id); setCameraJobId(item.id);
setCameraJob(item); setCameraJob(item);
navigation.navigate("MediaBrowserTab"); navigation.navigate("MediaBrowserTab");
_swipeableRow.current.close(); }}
}} >
> <Ionicons
<Ionicons style={[style, { alignSelf: "center" }]}
style={style} name="ios-add"
name="ios-add" size={32}
size={32} color="dodgerblue"
color="dodgerblue" />
/> </Button>
</Button> )}
)} />
/>
</TouchableOpacity>
); );
} }

View File

@@ -1,7 +1,7 @@
import { Ionicons } from "@expo/vector-icons";
import React from "react"; import React from "react";
import { Modal } from "react-native"; import { Modal, SafeAreaView, TouchableOpacity, View } from "react-native";
import Gallery from "react-native-image-gallery"; import Gallery from "react-native-image-gallery";
export default function MediaCacheOverlay({ export default function MediaCacheOverlay({
photos, photos,
previewVisible, previewVisible,
@@ -9,7 +9,6 @@ export default function MediaCacheOverlay({
imgIndex, imgIndex,
setImgIndex, setImgIndex,
}) { }) {
console.log("photos :>> ", photos);
return ( return (
<Modal <Modal
onDismiss={() => setPreviewVisible(false)} onDismiss={() => setPreviewVisible(false)}
@@ -17,7 +16,20 @@ export default function MediaCacheOverlay({
visible={previewVisible} visible={previewVisible}
transparent={false} transparent={false}
> >
<Gallery initialPage={imgIndex} style={{ flex: 1 }} images={photos} /> <SafeAreaView style={{ flex: 1, backgroundColor: "black" }}>
<Gallery initialPage={imgIndex} images={photos} />
<TouchableOpacity
style={{ position: "absolute" }}
onPress={() => setPreviewVisible(false)}
>
<Ionicons
name="ios-close"
size={64}
color="dodgerblue"
style={{ margin: 20 }}
/>
</TouchableOpacity>
</SafeAreaView>
</Modal> </Modal>
); );
} }

View File

@@ -2,7 +2,6 @@ import { Ionicons } from "@expo/vector-icons";
import { AssetsSelector } from "expo-images-picker"; import { AssetsSelector } from "expo-images-picker";
import * as MediaLibrary from "expo-media-library"; import * as MediaLibrary from "expo-media-library";
import _ from "lodash"; import _ from "lodash";
import plimit from "p-limit";
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { StyleSheet, Text, View } from "react-native"; import { StyleSheet, Text, View } from "react-native";
@@ -22,7 +21,7 @@ import CameraSelectJob from "../camera-select-job/camera-select-job.component";
import UploadDeleteSwitch from "../upload-delete-switch/upload-delete-switch.component"; import UploadDeleteSwitch from "../upload-delete-switch/upload-delete-switch.component";
import UploadProgress from "../upload-progress/upload-progress.component"; import UploadProgress from "../upload-progress/upload-progress.component";
const limit = plimit(2); //const limit = plimit(2);
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -52,50 +51,49 @@ export function ImageBrowserScreen({
async function handleOnSuccess(uri, id) { async function handleOnSuccess(uri, id) {
console.log("Succesful upload!", uri); console.log("Succesful upload!", uri);
logImEXEvent("imexmobile_successful_upload"); logImEXEvent("imexmobile_successful_upload");
if (deleteAfterUpload) {
try {
const result = await MediaLibrary.deleteAssetsAsync([id]);
console.log("Delete result :>> ", result);
} catch (error) {
console.log("Unable to delete picture.", error);
}
}
setUploads((prevUploads) => _.omit(prevUploads, uri)); setUploads((prevUploads) => _.omit(prevUploads, uri));
} }
const onDone = async (data) => { const onDone = async (data) => {
logImEXEvent("imexmobile_upload_documents", { count: data.length }); logImEXEvent("imexmobile_upload_documents", { count: data.length });
const actions = []; const ret = await Promise.all(
data.forEach(function (p) { data.map(async (p) => {
let filename; let filename;
//Appears to work for android. //Appears to work for android.
//iOS provides the filename, android doe snot. //iOS provides the filename, android doe snot.
filename = p.filename || p.uri.split("/").pop(); filename = p.filename || p.uri.split("/").pop();
actions.push( const result = await handleUpload(
limit( {
handleUpload( //iOS provides the file name. Android does not.
{ uri: p.uri,
//iOS provides the file name. Android does not. filename,
uri: p.uri, mediaId: p.id,
filename, onError: handleOnError,
onError: handleOnError, onProgress: ({ percent }) => handleOnProgress(filename, percent),
onProgress: ({ percent }) => handleOnProgress(filename, percent), onSuccess: () => handleOnSuccess(filename, p.id),
onSuccess: () => handleOnSuccess(filename, p.id), },
}, {
{ bodyshop: bodyshop,
bodyshop: bodyshop, jobId: selectedCameraJobId !== "temp" ? selectedCameraJobId : null,
jobId: uploaded_by: currentUser.email,
selectedCameraJobId !== "temp" ? selectedCameraJobId : null, photo: p,
uploaded_by: currentUser.email, }
photo: p, );
} return result;
) })
) );
);
}); if (deleteAfterUpload) {
await Promise.all(actions); try {
const result = await MediaLibrary.deleteAssetsAsync(
ret.map((r) => r.mediaId)
);
console.log("Delete result :>> ", result);
} catch (error) {
console.log("Unable to delete picture.", error);
}
}
forceRerender(); forceRerender();
//navigation.goBack();
}; };
return ( return (

View File

@@ -1,4 +1,5 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import _ from "lodash";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { import {
ScrollView, ScrollView,
@@ -8,7 +9,6 @@ import {
View, View,
} from "react-native"; } from "react-native";
import * as Progress from "react-native-progress"; import * as Progress from "react-native-progress";
import _ from "lodash";
export default function UploadProgress({ uploads, setUploads }) { export default function UploadProgress({ uploads, setUploads }) {
const uploadKeys = useMemo(() => { const uploadKeys = useMemo(() => {
if (uploads) return Object.keys(uploads); if (uploads) return Object.keys(uploads);
@@ -46,7 +46,6 @@ export default function UploadProgress({ uploads, setUploads }) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
display: "flex", display: "flex",
//flex: 1,
}, },
progressItem: { progressItem: {
display: "flex", display: "flex",

View File

@@ -1,19 +1,17 @@
//GQL Imports //GQL Imports
import { import {
ApolloClient, ApolloClient,
from,
HttpLink,
InMemoryCache, InMemoryCache,
split, split,
ApolloLink,
HttpLink,
from,
} from "@apollo/client"; } from "@apollo/client";
import { setContext } from "@apollo/client/link/context"; import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";
import { RetryLink } from "@apollo/client/link/retry"; import { RetryLink } from "@apollo/client/link/retry";
import { WebSocketLink } from "@apollo/client/link/ws"; import { WebSocketLink } from "@apollo/client/link/ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { auth } from "../firebase/firebase.utils";
import { onError } from "@apollo/client/link/error";
import { getMainDefinition } from "@apollo/client/utilities"; import { getMainDefinition } from "@apollo/client/utilities";
import { auth } from "../firebase/firebase.utils";
const httpLink = new HttpLink({ const httpLink = new HttpLink({
// uri: "https://bodyshop-dev-db.herokuapp.com/v1/graphql", // uri: "https://bodyshop-dev-db.herokuapp.com/v1/graphql",
@@ -53,7 +51,6 @@ const errorLink = onError(
); );
if (networkError) if (networkError)
console.error(`[Network error]: ${JSON.stringify(networkError)}`); console.error(`[Network error]: ${JSON.stringify(networkError)}`);
console.log(operation.getContext());
} }
); );

11
package-lock.json generated
View File

@@ -42,7 +42,7 @@
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz", "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz",
"react-native-easy-grid": "^0.2.2", "react-native-easy-grid": "^0.2.2",
"react-native-gesture-handler": "~1.8.0", "react-native-gesture-handler": "~1.8.0",
"react-native-image-gallery": "^2.1.5", "react-native-image-gallery": "meliorence/react-native-image-gallery#pull/152/head",
"react-native-indicators": "^0.17.0", "react-native-indicators": "^0.17.0",
"react-native-pager-view": "^5.1.0", "react-native-pager-view": "^5.1.0",
"react-native-paper": "^4.7.2", "react-native-paper": "^4.7.2",
@@ -13074,8 +13074,8 @@
}, },
"node_modules/react-native-image-gallery": { "node_modules/react-native-image-gallery": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-native-image-gallery/-/react-native-image-gallery-2.1.5.tgz", "resolved": "git+ssh://git@github.com/meliorence/react-native-image-gallery.git#06c9564e0f9978b1ed20306c8b1c7131620dcdeb",
"integrity": "sha512-xC7nuPu4GUH0da6byofQ10LjtqlKj+VaLc0NHBJmeMHVvdvmRvFEO6UOq0Q0m/ePx3OQiPTNwGbf5BSPJEKa0w==", "license": "ISC",
"dependencies": { "dependencies": {
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react-mixin": "^3.0.5", "react-mixin": "^3.0.5",
@@ -25332,9 +25332,8 @@
} }
}, },
"react-native-image-gallery": { "react-native-image-gallery": {
"version": "2.1.5", "version": "git+ssh://git@github.com/meliorence/react-native-image-gallery.git#06c9564e0f9978b1ed20306c8b1c7131620dcdeb",
"resolved": "https://registry.npmjs.org/react-native-image-gallery/-/react-native-image-gallery-2.1.5.tgz", "from": "react-native-image-gallery@meliorence/react-native-image-gallery#pull/152/head",
"integrity": "sha512-xC7nuPu4GUH0da6byofQ10LjtqlKj+VaLc0NHBJmeMHVvdvmRvFEO6UOq0Q0m/ePx3OQiPTNwGbf5BSPJEKa0w==",
"requires": { "requires": {
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react-mixin": "^3.0.5", "react-mixin": "^3.0.5",

View File

@@ -45,7 +45,7 @@
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz", "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz",
"react-native-easy-grid": "^0.2.2", "react-native-easy-grid": "^0.2.2",
"react-native-gesture-handler": "~1.8.0", "react-native-gesture-handler": "~1.8.0",
"react-native-image-gallery": "^2.1.5", "react-native-image-gallery": "meliorence/react-native-image-gallery#pull/152/head",
"react-native-indicators": "^0.17.0", "react-native-indicators": "^0.17.0",
"react-native-pager-view": "^5.1.0", "react-native-pager-view": "^5.1.0",
"react-native-paper": "^4.7.2", "react-native-paper": "^4.7.2",

View File

@@ -10,7 +10,7 @@ var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId); cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
export const handleUpload = async (ev, context) => { export const handleUpload = async (ev, context) => {
const { uri, filename, onError, onSuccess, onProgress } = ev; const { uri, filename, mediaId, onError, onSuccess, onProgress } = ev;
const { bodyshop, jobId } = context; const { bodyshop, jobId } = context;
const newFile = await (await fetch(uri)).blob(); const newFile = await (await fetch(uri)).blob();
@@ -20,8 +20,9 @@ export const handleUpload = async (ev, context) => {
/\.[^/.]+$/, /\.[^/.]+$/,
"" ""
)}`; )}`;
return uploadToCloudinary( const res = await uploadToCloudinary(
key, key,
mediaId,
extension, extension,
newFile.type, newFile.type,
newFile, newFile,
@@ -30,10 +31,12 @@ export const handleUpload = async (ev, context) => {
onProgress, onProgress,
context context
); );
return res;
}; };
export const uploadToCloudinary = async ( export const uploadToCloudinary = async (
key, key,
mediaId,
extension, extension,
fileType, fileType,
file, file,
@@ -175,9 +178,13 @@ export const uploadToCloudinary = async (
// message: JSON.stringify(JSON.stringify(documentInsert.errors)), // message: JSON.stringify(JSON.stringify(documentInsert.errors)),
// }), // }),
// }); // });
return { success: false, error: JSON.stringify(documentInsert.errors) }; return {
success: false,
error: JSON.stringify(documentInsert.errors),
mediaId,
};
} }
return { success: true }; return { success: true, mediaId };
}; };
export function DetermineFileType(filetype) { export function DetermineFileType(filetype) {

View File

@@ -7363,9 +7363,8 @@
"invariant" "^2.2.4" "invariant" "^2.2.4"
"prop-types" "^15.7.2" "prop-types" "^15.7.2"
"react-native-image-gallery@^2.1.5": "react-native-image-gallery@meliorence/react-native-image-gallery#pull/152/head":
"integrity" "sha512-xC7nuPu4GUH0da6byofQ10LjtqlKj+VaLc0NHBJmeMHVvdvmRvFEO6UOq0Q0m/ePx3OQiPTNwGbf5BSPJEKa0w==" "resolved" "git+ssh://git@github.com/meliorence/react-native-image-gallery.git#06c9564e0f9978b1ed20306c8b1c7131620dcdeb"
"resolved" "https://registry.npmjs.org/react-native-image-gallery/-/react-native-image-gallery-2.1.5.tgz"
"version" "2.1.5" "version" "2.1.5"
dependencies: dependencies:
"prop-types" "^15.6.0" "prop-types" "^15.6.0"