Remove delete on upload for android.
This commit is contained in:
2
app.json
2
app.json
@@ -6,7 +6,7 @@
|
|||||||
"scheme": "imex-mobile-scheme",
|
"scheme": "imex-mobile-scheme",
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
"extra": {
|
"extra": {
|
||||||
"expover": "19",
|
"expover": "21",
|
||||||
"eas": {
|
"eas": {
|
||||||
"projectId": "ffe01f3a-d507-4698-82cd-da1f1cad450b"
|
"projectId": "ffe01f3a-d507-4698-82cd-da1f1cad450b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function JobTabLayout() {
|
|||||||
headerShown: false,
|
headerShown: false,
|
||||||
animation: "fade",
|
animation: "fade",
|
||||||
tabBarStyle: {
|
tabBarStyle: {
|
||||||
marginTop: -48,
|
marginTop: -24,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1566,6 +1566,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>android_deletephotos</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>android_deletephotos_message</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>converting</name>
|
<name>converting</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
|
import { useTheme } from "@/hooks";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Button, Card, Text } from "react-native-paper";
|
import { Button, Card, Text } from "react-native-paper";
|
||||||
|
|
||||||
export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
|
export default function ErrorDisplay({ errorMessage, error, onDismiss }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<Card mode="outlined" style={{ margin: 8, backgroundColor: "#ffdddd" }}>
|
<Card
|
||||||
|
mode="outlined"
|
||||||
|
style={{ margin: 8, backgroundColor: theme?.colors?.errorContainer }}
|
||||||
|
>
|
||||||
<Card.Title title={t("general.labels.error")} titleVariant="titleLarge" />
|
<Card.Title title={t("general.labels.error")} titleVariant="titleLarge" />
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Text>
|
<Text>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useTheme } from "@/hooks";
|
||||||
import { selectDeleteAfterUpload } from "@/redux/app/app.selectors";
|
import { selectDeleteAfterUpload } from "@/redux/app/app.selectors";
|
||||||
import { signOutStart } from "@/redux/user/user.actions";
|
import { signOutStart } from "@/redux/user/user.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors";
|
||||||
@@ -10,7 +11,7 @@ import * as Notifications from "expo-notifications";
|
|||||||
import * as Updates from "expo-updates";
|
import * as Updates from "expo-updates";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Alert, ScrollView, StyleSheet, View } from "react-native";
|
import { Alert, Platform, ScrollView, StyleSheet, View } from "react-native";
|
||||||
import { Button, Card, Divider, List, Text } from "react-native-paper";
|
import { Button, Card, Divider, List, Text } from "react-native-paper";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -31,7 +32,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(Tab);
|
|||||||
function Tab({ bodyshop, currentUser, signOutStart }) {
|
function Tab({ bodyshop, currentUser, signOutStart }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [permissionState, setPermissionState] = useState(null);
|
const [permissionState, setPermissionState] = useState(null);
|
||||||
|
const theme = useTheme();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const status = await Notifications.getPermissionsAsync();
|
const status = await Notifications.getPermissionsAsync();
|
||||||
@@ -81,6 +82,18 @@ function Tab({ bodyshop, currentUser, signOutStart }) {
|
|||||||
</Text>
|
</Text>
|
||||||
<UploadDeleteSwitch />
|
<UploadDeleteSwitch />
|
||||||
</View>
|
</View>
|
||||||
|
{Platform.OS === "android" && (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
marginVertical: 8,
|
||||||
|
backgroundColor: theme?.colors?.errorContainer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{ margin: 8, fontStyle: "italic" }}>
|
||||||
|
{t("mediabrowser.labels.android_deletephotos_message")}
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
<List.Item
|
<List.Item
|
||||||
title="Media Storage"
|
title="Media Storage"
|
||||||
description={
|
description={
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Platform } from "react-native";
|
||||||
import { Switch } from "react-native-paper";
|
import { Switch } from "react-native-paper";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -22,7 +23,8 @@ export function UploadDeleteSwitch({
|
|||||||
onValueChange={() => {
|
onValueChange={() => {
|
||||||
toggleDeleteAfterUpload();
|
toggleDeleteAfterUpload();
|
||||||
}}
|
}}
|
||||||
value={deleteAfterUpload}
|
value={Platform.OS === "android" ? false : deleteAfterUpload}
|
||||||
|
disabled={Platform.OS === "android"}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import i18n from "@/translations/i18n";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import * as FileSystem from "expo-file-system/legacy";
|
import * as FileSystem from "expo-file-system/legacy";
|
||||||
import * as ImagePicker from "expo-image-picker";
|
import * as ImagePicker from "expo-image-picker";
|
||||||
@@ -11,6 +12,7 @@ import { client } from '../../graphql/client';
|
|||||||
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
|
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
|
||||||
import { axiosAuthInterceptorId } from "../../util/CleanAxios";
|
import { axiosAuthInterceptorId } from "../../util/CleanAxios";
|
||||||
import { fetchImageFromUri, replaceAccents } from '../../util/uploadUtils';
|
import { fetchImageFromUri, replaceAccents } from '../../util/uploadUtils';
|
||||||
|
import { toggleDeleteAfterUpload } from "../app/app.actions";
|
||||||
import { selectDeleteAfterUpload } from "../app/app.selectors";
|
import { selectDeleteAfterUpload } from "../app/app.selectors";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../user/user.selectors";
|
||||||
@@ -411,60 +413,69 @@ function* mediaUploadCompletedAction({ payload: photos }) {
|
|||||||
const filesToDelete = Object.keys(progress).filter((key) => progress[key].status === 'completed').map((key) => progress[key]);
|
const filesToDelete = Object.keys(progress).filter((key) => progress[key].status === 'completed').map((key) => progress[key]);
|
||||||
|
|
||||||
if (Platform.OS === "android") {
|
if (Platform.OS === "android") {
|
||||||
yield MediaLibrary.getPermissionsAsync(false);
|
Alert.alert(
|
||||||
const asset = filesToDelete[0];
|
i18n.t("mediabrowser.labels.android_deletephotos"),
|
||||||
let assetIdToDelete = asset.assetId;
|
i18n.t("mediabrowser.labels.android_deletephotos_message"),)
|
||||||
|
|
||||||
// 2. ANDROID FIX: Find the original asset ID
|
//Set delete on upload to false
|
||||||
if (!assetIdToDelete && Platform.OS === 'android') {
|
yield put(toggleDeleteAfterUpload()) //Toggle is fine - if we got here, it's true.
|
||||||
// Fetch the last 50 images from the gallery
|
|
||||||
const recentAssets = yield call(MediaLibrary.getAssetsAsync, {
|
|
||||||
first: 50,
|
|
||||||
sortBy: [MediaLibrary.SortBy.creationTime],
|
|
||||||
mediaType: MediaLibrary.MediaType.photo,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Try to match based on width, height, and proximity of creation time
|
// //With new Android restrictions, we can't do this because we don't get the asset ID back from the picker.
|
||||||
// Note: The cache file timestamp might differ slightly from the original
|
// //The workarounds below don't work currently. Leaving code for reference.
|
||||||
const foundAsset = recentAssets.assets.find(libraryItem => {
|
// yield MediaLibrary.getPermissionsAsync(false);
|
||||||
console.log("Comparing library item:", moment(asset.exif.DateTime, "YYYY:MM:DD HH:mm:ss").valueOf(), libraryItem.creationTime);
|
// const asset = filesToDelete[0];
|
||||||
return (
|
// let assetIdToDelete = asset.assetId;
|
||||||
libraryItem.width === asset.exif.ImageWidth &&
|
|
||||||
libraryItem.height === asset.exif.ImageLength &&
|
|
||||||
Math.abs(moment(asset.exif.DateTimeOriginal, "YYYY:MM:DD HH:mm:ss").valueOf() - libraryItem.creationTime) < 1000
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (foundAsset) {
|
// // 2. ANDROID FIX: Find the original asset ID
|
||||||
assetIdToDelete = foundAsset.id;
|
// if (!assetIdToDelete && Platform.OS === 'android') {
|
||||||
}
|
// // Fetch the last 50 images from the gallery
|
||||||
}
|
// const recentAssets = yield call(MediaLibrary.getAssetsAsync, {
|
||||||
|
// first: 50,
|
||||||
|
// sortBy: [MediaLibrary.SortBy.creationTime],
|
||||||
|
// mediaType: MediaLibrary.MediaType.photo,
|
||||||
|
// });
|
||||||
|
|
||||||
// 3. Upload and Delete
|
// // Try to match based on width, height, and proximity of creation time
|
||||||
if (assetIdToDelete) {
|
// // Note: The cache file timestamp might differ slightly from the original
|
||||||
// await uploadFunction(asset.uri);
|
// const foundAsset = recentAssets.assets.find(libraryItem => {
|
||||||
yield call(MediaLibrary.deleteAssetsAsync, assetIdToDelete);
|
// console.log("Comparing library item:", moment(asset.exif.DateTime, "YYYY:MM:DD HH:mm:ss").valueOf(), libraryItem.creationTime);
|
||||||
}
|
// return (
|
||||||
|
// libraryItem.width === asset.exif.ImageWidth &&
|
||||||
|
// libraryItem.height === asset.exif.ImageLength &&
|
||||||
|
// Math.abs(moment(asset.exif.DateTimeOriginal, "YYYY:MM:DD HH:mm:ss").valueOf() - libraryItem.creationTime) < 1000
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (foundAsset) {
|
||||||
|
// assetIdToDelete = foundAsset.id;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 3. Upload and Delete
|
||||||
|
// if (assetIdToDelete) {
|
||||||
|
// // await uploadFunction(asset.uri);
|
||||||
|
// yield call(MediaLibrary.deleteAssetsAsync, assetIdToDelete);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Create a new asset with the first file to delete.
|
// //Create a new asset with the first file to delete.
|
||||||
// console.log('Trying new delete.');
|
// // console.log('Trying new delete.');
|
||||||
|
|
||||||
const album = yield call(MediaLibrary.createAlbumAsync,
|
// const album = yield call(MediaLibrary.createAlbumAsync,
|
||||||
"ImEX Mobile Deleted",
|
// "ImEX Mobile Deleted",
|
||||||
filesToDelete.pop().assetId,
|
// filesToDelete.pop().assetId,
|
||||||
false
|
// false
|
||||||
);
|
// );
|
||||||
//Move the rest.
|
// //Move the rest.
|
||||||
if (filesToDelete.length > 0) {
|
// if (filesToDelete.length > 0) {
|
||||||
const moveResult = yield call(MediaLibrary.addAssetsToAlbumAsync,
|
// const moveResult = yield call(MediaLibrary.addAssetsToAlbumAsync,
|
||||||
filesToDelete.map(f => f.assetId),
|
// filesToDelete.map(f => f.assetId),
|
||||||
album,
|
// album,
|
||||||
false
|
// false
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
yield call(MediaLibrary.deleteAlbumsAsync, album);
|
// yield call(MediaLibrary.deleteAlbumsAsync, album);
|
||||||
} else {
|
} else {
|
||||||
yield call(MediaLibrary.deleteAssetsAsync, filesToDelete.map(f => f.assetId));
|
yield call(MediaLibrary.deleteAssetsAsync, filesToDelete.map(f => f.assetId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,8 @@
|
|||||||
"upload": "Upload"
|
"upload": "Upload"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"android_deletephotos": "Unable to Delete Photos",
|
||||||
|
"android_deletephotos_message": "Due to updated security restrictions on Android, media cannot be automatically deleted. Please delete media manually. ",
|
||||||
"converting": "Converting",
|
"converting": "Converting",
|
||||||
"deleteafterupload": "Delete After Upload",
|
"deleteafterupload": "Delete After Upload",
|
||||||
"localserver": "Local Server URL: {{url}}",
|
"localserver": "Local Server URL: {{url}}",
|
||||||
|
|||||||
@@ -107,6 +107,8 @@
|
|||||||
"upload": ""
|
"upload": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"android_deletephotos": "",
|
||||||
|
"android_deletephotos_message": "",
|
||||||
"converting": "",
|
"converting": "",
|
||||||
"deleteafterupload": "",
|
"deleteafterupload": "",
|
||||||
"localserver": "",
|
"localserver": "",
|
||||||
|
|||||||
@@ -107,6 +107,8 @@
|
|||||||
"upload": ""
|
"upload": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"android_deletephotos": "",
|
||||||
|
"android_deletephotos_message": "",
|
||||||
"converting": "",
|
"converting": "",
|
||||||
"deleteafterupload": "",
|
"deleteafterupload": "",
|
||||||
"localserver": "",
|
"localserver": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user