diff --git a/App.js b/App.js
index d41b9e5..e2fa215 100644
--- a/App.js
+++ b/App.js
@@ -1,19 +1,27 @@
import { ApolloProvider } from "@apollo/client";
+import * as Sentry from "@sentry/react-native";
+import { SplitFactory } from "@splitsoftware/splitio-react-native";
+import "expo-asset";
+import "intl";
+import "intl/locale-data/jsonp/en";
import React from "react";
-import { MD2LightTheme as DefaultTheme, Provider as PaperProvider } from "react-native-paper";
+import {
+ MD2LightTheme as DefaultTheme,
+ Provider as PaperProvider,
+} from "react-native-paper";
+import { SafeAreaProvider } from "react-native-safe-area-context";
+import Toast from "react-native-toast-message";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
-import * as Sentry from '@sentry/react-native';
import ScreenMainComponent from "./components/screen-main/screen-main.component";
import { logImEXEvent } from "./firebase/firebase.analytics";
import { client } from "./graphql/client";
import { persistor, store } from "./redux/store";
-import "intl";
-import "intl/locale-data/jsonp/en";
import "./translations/i18n";
-import "expo-asset";
-import Toast from "react-native-toast-message";
-import { SafeAreaProvider } from "react-native-safe-area-context";
+
+import RNEventSource from "react-native-event-source";
+globalThis.EventSource = RNEventSource;
+
Sentry.init({
dsn: "https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.sentry.io/5558869",
enableInExpoDevelopment: true,
@@ -27,7 +35,6 @@ Sentry.init({
debug: true, // Sentry will try to print out useful debugging information if something goes wrong with sending an event. Set this to `false` in production.
});
-
const theme = {
...DefaultTheme,
colors: {
diff --git a/app.json b/app.json
index 6830cc2..60c692a 100644
--- a/app.json
+++ b/app.json
@@ -16,8 +16,11 @@
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.imex.imexmobile",
- "buildNumber": "1",
+ "buildNumber": "3",
"googleServicesFile": "./GoogleService-Info.plist",
+ "entitlements": {
+ "aps-environment": "development"
+ },
"infoPlist": {
"NSPhotoLibraryUsageDescription": "Allow $(PRODUCT_NAME) to access your photos.",
"NSPhotoLibraryAddUsageDescription": "Allow $(PRODUCT_NAME) to save photos.",
@@ -26,7 +29,7 @@
},
"android": {
"package": "com.imex.imexmobile",
- "versionCode": 1100035,
+ "versionCode": 1100036,
"googleServicesFile": "./google-services.json",
"permissions": [
"android.permission.READ_EXTERNAL_STORAGE",
diff --git a/components/job-documents/job-documents.component.jsx b/components/job-documents/job-documents.component.jsx
index 4466328..783db8a 100644
--- a/components/job-documents/job-documents.component.jsx
+++ b/components/job-documents/job-documents.component.jsx
@@ -1,4 +1,5 @@
-import React, { useMemo, useState } from "react";
+import axios from "axios";
+import React, { useEffect, useState } from "react";
import {
FlatList,
Image,
@@ -10,12 +11,12 @@ import {
import env from "../../env";
import { DetermineFileType } from "../../util/document-upload.utility";
import MediaCacheOverlay from "../media-cache-overlay/media-cache-overlay.component";
-import { useEffect } from "react";
-import axios from "axios";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
+import { splitClient } from "../screen-main/screen-main.component";
+
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -32,13 +33,16 @@ export function JobDocumentsComponent({ bodyshop, job, loading, refetch }) {
const [previewVisible, setPreviewVisible] = useState(false);
const [fullphotos, setFullPhotos] = useState([]);
const [imgIndex, setImgIndex] = useState(0);
+
+ const useImgproxy = splitClient.getTreatment("Imgproxy");
+
const onRefresh = async () => {
return refetch();
};
useEffect(() => {
async function getPhotos() {
- if (bodyshop.localmediatoken === "imgproxy") {
+ if (useImgproxy) {
const result = await axios.post(
`${env.API_URL}/media/imgproxy/thumbnails`,
{
@@ -128,7 +132,11 @@ export function JobDocumentsComponent({ bodyshop, job, loading, refetch }) {
)}
/>
- {fullphotos.length}
+
+ {fullphotos.length}
+
{
- return bodyshop.uselocalmediaserver
- ? JobDocumentsLocalComponent({
- job: data.jobs_by_pk,
-
- bodyshop: bodyshop,
- })
- : JobDocuments({
- job: data.jobs_by_pk,
- loading: loading,
- refetch: refetch,
- });
+ return bodyshop.uselocalmediaserver ? (
+
+ ) : (
+
+ );
},
notes: () =>
diff --git a/components/screen-main/screen-main.component.jsx b/components/screen-main/screen-main.component.jsx
index 5a9dfb2..b0acc6b 100644
--- a/components/screen-main/screen-main.component.jsx
+++ b/components/screen-main/screen-main.component.jsx
@@ -19,12 +19,14 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
+import env from "../../env";
import ScreenJobDetail from "../screen-job-detail/screen-job-detail.component";
import ScreenJobList from "../screen-job-list/screen-job-list.component";
import ScreenMediaBrowser from "../screen-media-browser/screen-media-browser.component";
import ScreenSettingsComponent from "../screen-settings/screen-settings.component";
import ScreenSignIn from "../screen-sign-in/screen-sign-in.component";
import ScreenSplash from "../screen-splash/screen-splash.component";
+import { SplitFactory } from "@splitsoftware/splitio-react-native";
const ActiveJobStack = createNativeStackNavigator();
const MoreStack = createNativeStackNavigator();
@@ -148,6 +150,8 @@ const BottomTabsNavigator = () => (
);
+export var splitClient;
+
export function ScreenMainComponent({
checkUserSession,
currentUser,
@@ -157,6 +161,16 @@ export function ScreenMainComponent({
checkUserSession();
}, [checkUserSession]);
+ useEffect(() => {
+ if (bodyshop && bodyshop.imexshopid) {
+ splitClient = SplitFactory({
+ //debug: true,
+ core: { authorizationKey: env.SPLIT_API, key: bodyshop.imexshopid },
+ }).client();
+ splitClient.setAttribute("imexshopid", bodyshop.imexshopid);
+ }
+ }, [bodyshop]);
+
return (
{currentUser.authorized === null ? (
diff --git a/components/upload-progress/upload-progress.component.jsx b/components/upload-progress/upload-progress.component.jsx
index 0cf774f..a8465a8 100644
--- a/components/upload-progress/upload-progress.component.jsx
+++ b/components/upload-progress/upload-progress.component.jsx
@@ -271,8 +271,7 @@ export function UploadProgress({
jobId: selectedCameraJobId !== "temp" ? selectedCameraJobId : null,
uploaded_by: currentUser.email,
photo: p,
- },
- bodyshop.localmediatoken === "imgproxy" ? "imgproxy" : "" //Choose which destination to use.
+ }
);
};
diff --git a/eas.json b/eas.json
index 010e85f..4bef6ef 100644
--- a/eas.json
+++ b/eas.json
@@ -7,8 +7,13 @@
"developmentClient": true,
"channel": "test",
"distribution": "internal",
- "ios": { //"simulator": true
- }
+ "ios": {}
+ },
+ "development-simulator": {
+ "developmentClient": true,
+ "channel": "test",
+ "distribution": "internal",
+ "ios": { "simulator": true }
},
"test": {
"channel": "test"
diff --git a/env.js b/env.js
index 9e39d65..cbfc72e 100644
--- a/env.js
+++ b/env.js
@@ -10,6 +10,7 @@ const ENV = {
REACT_APP_CLOUDINARY_ENDPOINT: "https://res.cloudinary.com/bodyshop",
REACT_APP_CLOUDINARY_API_KEY: "473322739956866",
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS: "c_fill,h_250,w_250",
+ SPLIT_API: "ts615lqgnmk84thn72uk18uu5pgce6e0l4rc",
firebase: {
apiKey: "AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c",
authDomain: "imex-test.firebaseapp.com",
@@ -29,6 +30,7 @@ const ENV = {
"https://api.cloudinary.com/v1_1/bodyshop",
REACT_APP_CLOUDINARY_ENDPOINT: "https://res.cloudinary.com/bodyshop",
REACT_APP_CLOUDINARY_API_KEY: "473322739956866",
+ SPLIT_API: "et9pjkik6bn67he5evpmpr1agoo7gactphgk",
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS: "c_fill,h_250,w_250",
firebase: {
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
diff --git a/graphql/bodyshop.queries.js b/graphql/bodyshop.queries.js
index eaf877f..cca90d8 100644
--- a/graphql/bodyshop.queries.js
+++ b/graphql/bodyshop.queries.js
@@ -11,6 +11,7 @@ export const QUERY_BODYSHOP = gql`
shopname
features
localmediatoken
+ imexshopid
}
}
`;
diff --git a/package-lock.json b/package-lock.json
index 450db8f..18d3f05 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,12 @@
{
"name": "imexmobile",
+ "version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
+ "name": "imexmobile",
+ "version": "1.0.0",
"dependencies": {
"@apollo/client": "^3.12.11",
"@babel/preset-env": "7.26.8",
@@ -18,6 +21,7 @@
"@react-navigation/native-stack": "^7.2.0",
"@react-navigation/stack": "^7.1.1",
"@sentry/react-native": "~6.3.0",
+ "@splitsoftware/splitio-react-native": "^1.1.0",
"axios": "^1.7.9",
"cloudinary-core": "^2.13.1",
"dinero.js": "^1.9.1",
@@ -54,6 +58,7 @@
"react-native": "0.76.7",
"react-native-draggable-flatlist": "^4.0.1",
"react-native-element-dropdown": "^2.12.4",
+ "react-native-event-source": "^1.1.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-image-gallery": "^2.1.5",
"react-native-image-viewing": "^0.2.2",
@@ -6145,6 +6150,37 @@
"@sinonjs/commons": "^3.0.0"
}
},
+ "node_modules/@splitsoftware/splitio-commons": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0.tgz",
+ "integrity": "sha512-7SJRBia0Pi72s76drH8kG2cVnCqkjMHMJQWJSFnG+rE/UOx9AROmuviOkY6tv6qYPJFqFQQGHGX6lXjxZhYzkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/ioredis": "^4.28.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "ioredis": "^4.28.0"
+ },
+ "peerDependenciesMeta": {
+ "ioredis": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@splitsoftware/splitio-react-native": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-react-native/-/splitio-react-native-1.1.0.tgz",
+ "integrity": "sha512-KaXU1KB+oDC309/rc0Wq47JbM2YOgk2EohhVwpwBK5awoKUKZyW/Ne6euzknkKdTBy3kAOJlh3wI9aMkvBAgpA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@splitsoftware/splitio-commons": "2.1.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -6222,6 +6258,15 @@
"hoist-non-react-statics": "^3.3.0"
}
},
+ "node_modules/@types/ioredis": {
+ "version": "4.28.10",
+ "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz",
+ "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -14325,6 +14370,12 @@
"react-native": "*"
}
},
+ "node_modules/react-native-event-source": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-event-source/-/react-native-event-source-1.1.0.tgz",
+ "integrity": "sha512-CAs76IW8kTrdy0okfV2KPopm7E9TL0uNAR+SRrN7iZ/ii0zBeHWuhD4Q2F8gRKvmkrEtCZ6uwnfYL2TFmK0QZg==",
+ "license": "MIT"
+ },
"node_modules/react-native-gesture-handler": {
"version": "2.20.2",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.20.2.tgz",
diff --git a/package.json b/package.json
index f2f04c4..ea96a1d 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@react-navigation/native-stack": "^7.2.0",
"@react-navigation/stack": "^7.1.1",
"@sentry/react-native": "~6.3.0",
+ "@splitsoftware/splitio-react-native": "^1.1.0",
"axios": "^1.7.9",
"cloudinary-core": "^2.13.1",
"dinero.js": "^1.9.1",
@@ -65,6 +66,7 @@
"react-native": "0.76.7",
"react-native-draggable-flatlist": "^4.0.1",
"react-native-element-dropdown": "^2.12.4",
+ "react-native-event-source": "^1.1.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-image-gallery": "^2.1.5",
"react-native-image-viewing": "^0.2.2",
diff --git a/util/document-upload.utility.js b/util/document-upload.utility.js
index 1134c0e..02b2fb8 100644
--- a/util/document-upload.utility.js
+++ b/util/document-upload.utility.js
@@ -5,14 +5,14 @@ import env from "../env";
import { client } from "../graphql/client";
import { INSERT_NEW_DOCUMENT } from "../graphql/documents.queries";
import { axiosAuthInterceptorId } from "./CleanAxios";
-
+import { splitClient } from "../components/screen-main/screen-main.component";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
//Required to prevent headers from getting set and rejected from Cloudinary.
var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
-export const handleUpload = async (ev, context, destination) => {
+export const handleUpload = async (ev, context) => {
const { mediaId, onError, onSuccess, onProgress } = ev;
const { bodyshop, jobId } = context;
@@ -22,6 +22,10 @@ export const handleUpload = async (ev, context, destination) => {
).blob();
let extension = imageData.localUri.split(".").pop();
+ //Default to Cloudinary in case of split treatment errors.
+ let destination =
+ splitClient?.getTreatment("Imgproxy") === "on" ? "imgproxy" : "cloudinary";
+
let key =
destination === "imgproxy"
? `${bodyshop.id}/${jobId}/${replaceAccents(