Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added configs/e2e/mendix-native-0.6.0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion configs/e2e/mendix-versions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"latest": "11.12.3"
"latest": "11.14.0"
}
1 change: 1 addition & 0 deletions configs/jsactions/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export default async args => {

const nativeExternal = [
/^mendix\//,
/^mendix-native(\/|$)/,
/^react-native(\/|$)/,
/^react-native-windows(\/|$)/,
/^react-native-web(\/|$)/,
Expand Down
3 changes: 2 additions & 1 deletion packages/jsActions/mobile-resources-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"marketplace": {
"name": "Native Mobile Resources",
"description": "Download this module to access a rich set of widgets and nanoflow actions created for native mobile, including authentication, network, platform, and more. A must-have for native mobile development!",
"minimumMXVersion": "11.12.3",
"minimumMXVersion": "11.14.0",
"marketplaceId": 109513
},
"testProject": {
Expand All @@ -34,6 +34,7 @@
"@sbaiahmed1/react-native-biometrics": "0.15.0",
"@swan-io/react-native-browser": "1.0.1",
"fbjs": "3.0.4",
"mendix-native": "https://github.com/mendix/mendix-native/releases/download/v0.6.0/mendix-native-v0.6.0.tgz",
"mime": "3.0.0",
"react-native-blob-util": "0.24.7",
"react-native-device-info": "15.0.2",
Expand Down
107 changes: 13 additions & 94 deletions packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import { Big } from "big.js";
import { Alert, Linking, NativeModules, Platform } from "react-native";
import { Alert, Linking, Platform } from "react-native";
import {
CameraOptions,
ErrorCode,
Expand All @@ -16,9 +16,10 @@ import {
launchImageLibrary
} from "react-native-image-picker";
import { getLocales } from "react-native-localize";
import { ImagePickerV2Options, ImagePickerV2Response, PictureQuality, PictureSource } from "../../typings/Camera";
import { PictureQuality, PictureSource } from "../../typings/Camera";

// BEGIN EXTRA CODE
const loadMendixNative = () => import("mendix-native" + "");
// END EXTRA CODE

/**
Expand Down Expand Up @@ -56,8 +57,8 @@ export async function TakePicture(
}

// V3 dropped the feature of providing an action sheet so users can decide on which action to take, camera or library.
const nativeVersionMajor = NativeModules?.ImagePickerManager?.showImagePicker ? 2 : 4;
const RNPermissions = nativeVersionMajor === 4 ? (await import("react-native-permissions")).default : null;
// react-native-image-picker v7.2.3 is always v4+ (no legacy action sheet API)
const RNPermissions = (await import("react-native-permissions")).default;

try {
const uri = await takePicture();
Expand All @@ -77,32 +78,14 @@ export async function TakePicture(

function takePicture(): Promise<string | undefined> {
return new Promise((resolve, reject) => {
const options = nativeVersionMajor === 2 ? getOptionsV2() : getOptions();
const options = getOptions();
getPictureMethod()
.then(method =>
method(options, (response: ImagePickerV2Response | ImagePickerResponse) => {
method(options, (response: ImagePickerResponse) => {
if (response.didCancel) {
return resolve(undefined);
}

if (nativeVersionMajor === 2) {
const responseV2 = response as ImagePickerV2Response;

if (responseV2.error) {
const unhandledError = handleImagePickerV2Error(responseV2.error);

if (!unhandledError) {
return resolve(undefined);
}

return reject(new Error(responseV2.error));
}

return resolve(responseV2.uri);
}

response = response as ImagePickerResponse;

if (response.errorCode) {
handleImagePickerV4Error(response.errorCode, response.errorMessage);

Expand All @@ -118,7 +101,8 @@ export async function TakePicture(

async function safeRemove(filePath: string): Promise<void> {
try {
await NativeModules.MxFileSystem.remove(filePath);
const { NativeFileSystem } = await loadMendixNative();
await NativeFileSystem.remove(filePath);
} catch (error) {
console.warn(`Failed to remove file at ${filePath}. Error: ${error}`);
// ignore error
Expand All @@ -127,7 +111,8 @@ export async function TakePicture(

function storeFile(imageObject: mendix.lib.MxObject, uri: string): Promise<boolean> {
return new Promise((resolve, reject) => {
NativeModules.MxFileSystem.read(uri.replace("file://", ""))
loadMendixNative()
.then(({ NativeFileSystem }) => NativeFileSystem.read(uri.replace("file://", "")))
.then((nativeBlob: unknown) => {
const blob = new Blob();
Object.assign(blob, { data: nativeBlob });
Expand Down Expand Up @@ -169,13 +154,10 @@ export async function TakePicture(
}

async function getPictureMethod(): Promise<
(
options: ImagePickerV2Options | CameraOptions | ImageLibraryOptions,
callback: (response: ImagePickerV2Response | ImagePickerResponse) => void
) => void
(options: CameraOptions | ImageLibraryOptions, callback: (response: ImagePickerResponse) => void) => void
> {
async function handleCameraRequest(): Promise<typeof launchCamera> {
if (Platform.OS === "android" && nativeVersionMajor === 4) {
if (Platform.OS === "android") {
await checkAndMaybeRequestAndroidPermission();
}

Expand Down Expand Up @@ -225,38 +207,6 @@ export async function TakePicture(
}
}

function getOptionsV2(): ImagePickerV2Options {
const { maxWidth, maxHeight } = getPictureQuality();
const [language] = getLocales().map(local => local.languageCode);
const isDutch = language === "nl";

return {
mediaType: "photo" as const,
maxWidth,
maxHeight,
noData: true,
title: isDutch ? "Foto toevoegen" : "Select a photo",
cancelButtonTitle: isDutch ? "Annuleren" : "Cancel",
takePhotoButtonTitle: isDutch ? "Foto maken" : "Take photo",
chooseFromLibraryButtonTitle: isDutch ? "Kies uit bibliotheek" : "Choose from library",
permissionDenied: {
title: isDutch
? "Deze app heeft geen toegang tot uw camera of foto bibliotheek"
: "This app does not have access to your camera or photo library",
text: isDutch
? "Ga naar Instellingen > Privacy om toegang tot uw camera en bestanden te verlenen."
: "To enable access, tap Settings > Privacy and turn on Camera and Photos/Storage.",
reTryTitle: isDutch ? "Instellingen" : "Settings",
okTitle: isDutch ? "Annuleren" : "Cancel"
},
storageOptions: {
skipBackup: true,
cameraRoll: false,
privateDirectory: true
}
};
}

function getOptions(): CameraOptions | ImageLibraryOptions {
const { maxWidth, maxHeight } = getPictureQuality();
return {
Expand Down Expand Up @@ -293,37 +243,6 @@ export async function TakePicture(
}
}

function handleImagePickerV2Error(error: string): string | undefined {
const ERRORS = {
AndroidPermissionDenied: "Permissions weren't granted",
iOSPhotoLibraryPermissionDenied: "Photo library permissions not granted",
iOSCameraPermissionDenied: "Camera permissions not granted"
};

switch (error) {
case ERRORS.iOSPhotoLibraryPermissionDenied:
showAlert(
"This app does not have access to your photo library",
"To enable access, tap Settings and turn on Photos."
);
return;

case ERRORS.iOSCameraPermissionDenied:
showAlert(
"This app does not have access to your camera",
"To enable access, tap Settings and turn on Camera."
);
return;

case ERRORS.AndroidPermissionDenied:
// Ignore this error because the image picker plugin already shows an alert in this case.
return;

default:
return error;
}
}

function showAlert(title: string, message: string): void {
Alert.alert(
title,
Expand Down
Loading
Loading