Skip to content

Commit d941bd2

Browse files
authored
Merge branch 'main' into moo/MOO-2308-replace-notifee-library-11.13
2 parents 7d7086c + 8ce5083 commit d941bd2

10 files changed

Lines changed: 72 additions & 17 deletions

File tree

packages/jsActions/mobile-resources-native/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1212
- We switched to a new sound library for the Play sound action to support react-native 0.84+.
1313
- The Play sound action now plays audio files from online (network) documents on Android by downloading them to a version-based cache before playback.
1414
- We have fixed the biometric authentication issue where it was not working on Android and crashing on iOS.
15+
- Fixed an issue where the `TakePicture` and `TakePictureAdvanced` actions failed to capture photos on Android.
1516

1617
## [12.1.0] Native Mobile Resources - 2026-6-10
1718

packages/jsActions/mobile-resources-native/src/camera/TakePicture.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,20 @@ export async function TakePicture(
118118

119119
function storeFile(imageObject: mendix.lib.MxObject, uri: string): Promise<boolean> {
120120
return new Promise((resolve, reject) => {
121-
fetch(uri)
122-
.then(response => response.blob())
123-
.then(blob => {
121+
NativeModules.MxFileSystem.read(uri.replace("file://", ""))
122+
.then((nativeBlob: unknown) => {
123+
const blob = new Blob();
124+
Object.assign(blob, { data: nativeBlob });
124125
// eslint-disable-next-line no-useless-escape
125126
const filename = /[^\/]*$/.exec(uri)![0];
126127
const filePathWithoutFileScheme = uri.replace("file://", "");
127128

129+
// Set nativePayload so the patched FormData.prototype.append in NativeFileBackend
130+
// replaces the blob value with { uri, name, type } for online uploads. The patch
131+
// reads the third append() argument (fileName) and writes it onto nativePayload.name,
132+
// which FormData.getParts() uses as the Content-Disposition filename.
133+
(blob as any).nativePayload = { uri: `file://${uri}`, name: filename, type: "*/*" };
134+
128135
mx.data.saveDocument(
129136
imageObject.getGuid(),
130137
filename,

packages/jsActions/mobile-resources-native/src/camera/TakePictureAdvanced.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,20 @@ export async function TakePictureAdvanced(
171171

172172
function storeFile(imageObject: mendix.lib.MxObject, uri: string): Promise<boolean> {
173173
return new Promise((resolve, reject) => {
174-
fetch(uri)
175-
.then(response => response.blob())
176-
.then(blob => {
174+
NativeModules.MxFileSystem.read(uri.replace("file://", ""))
175+
.then((nativeBlob: unknown) => {
176+
const blob = new Blob();
177+
Object.assign(blob, { data: nativeBlob });
177178
// eslint-disable-next-line no-useless-escape
178179
const filename = /[^\/]*$/.exec(uri)![0];
179180
const filePathWithoutFileScheme = uri.replace("file://", "");
180181

182+
// Set nativePayload so the patched FormData.prototype.append in NativeFileBackend
183+
// replaces the blob value with { uri, name, type } for online uploads. The patch
184+
// reads the third append() argument (fileName) and writes it onto nativePayload.name,
185+
// which FormData.getParts() uses as the Content-Disposition filename.
186+
(blob as any).nativePayload = { uri: `file://${uri}`, name: filename, type: "*/*" };
187+
181188
mx.data.saveDocument(
182189
imageObject.getGuid(),
183190
filename,

packages/jsActions/nanoflow-actions-native/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
- Fixed an issue where base64-to-image decoding was failing.
10+
11+
## [7.2.0] Nanoflow Commons - 2026-7-3
12+
913
- Close offline database connection before navigating between pages with OpenURL nanoflow action.
1014

1115
## [7.1.0] Nanoflow Commons - 2026-6-5

packages/jsActions/nanoflow-actions-native/src/other/Base64DecodeToImage.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// Other code you write will be lost the next time you deploy the project.
88
import { Base64 } from "js-base64";
99
import RNBlobUtil from "react-native-blob-util";
10+
import { NativeModules } from "react-native";
1011

1112
// BEGIN EXTRA CODE
1213
// END EXTRA CODE
@@ -45,21 +46,38 @@ export async function Base64DecodeToImage(base64: string, image: mendix.lib.MxOb
4546
}
4647

4748
// Create a temporary file path
48-
const tempPath = `${RNBlobUtil.fs.dirs.CacheDir}/temp_image_${Date.now()}.png`;
49+
const fileName = `image_${Date.now()}.png`;
50+
const tempPath = `${RNBlobUtil.fs.dirs.CacheDir}/${fileName}`;
4951

5052
// Write Base64 data to a temporary file
5153
await RNBlobUtil.fs.writeFile(tempPath, cleanBase64, "base64");
5254

53-
// Fetch the file as a blob
54-
const res = await fetch(`file://${tempPath}`);
55-
const blob = await res.blob();
55+
// Read the file into the native blob store so offline mode works:
56+
// NativeFileBackend.storeFile calls NativeFileSystem.save(blob.data, path)
57+
// and blob.close() — a plain object has no .data getter or .close(), which
58+
// crashes iOS via [NSInvocation invokeWithTarget:].
59+
const nativeBlob = await NativeModules.MxFileSystem.read(tempPath.replace("file://", ""));
60+
// Normalize: MxFileSystem.read may return 'length' instead of 'size'.
61+
const blobData = { ...(nativeBlob as any) };
62+
if (blobData.size === undefined && blobData.length !== undefined) {
63+
blobData.size = blobData.length;
64+
}
65+
const blob = new Blob();
66+
Object.assign(blob, { data: blobData });
67+
68+
// Set nativePayload so the patched FormData.prototype.append in NativeFileBackend
69+
// replaces the blob value with { uri, name, type } for online uploads. The patch
70+
// reads the third append() argument (fileName) and writes it onto nativePayload.name,
71+
// which FormData.getParts() uses as the Content-Disposition filename.
72+
(blob as any).nativePayload = { uri: `file://${tempPath}`, name: fileName, type: "image/png" };
73+
const fileBlob = blob as Blob;
5674

5775
return new Promise((resolve, reject) => {
5876
mx.data.saveDocument(
5977
image.getGuid(),
60-
"camera image",
78+
fileName,
6179
{},
62-
blob,
80+
fileBlob,
6381
() => {
6482
RNBlobUtil.fs.unlink(tempPath).catch(e => console.info("Temp file cleanup failed:", e));
6583
resolve(true);

packages/pluggableWidgets/image-native/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- We fixed an issue that caused images from entities to not render on Android in Online Synchronization mode
12+
913
## [3.1.1] - 2026-6-10
1014

1115
### Changed

packages/pluggableWidgets/image-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "image-native",
33
"widgetName": "Image",
4-
"version": "3.1.1",
4+
"version": "3.1.2",
55
"description": "Display an image and enlarge it on click",
66
"copyright": "© Mendix Technology BV 2022. All rights reserved.",
77
"license": "Apache-2.0",

packages/pluggableWidgets/image-native/src/components/ImageIconSVG.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FunctionComponent, Fragment, useCallback } from "react";
2-
import { View } from "react-native";
2+
import { ImageURISource, Platform, View } from "react-native";
33
import { SvgUri, SvgXml } from "react-native-svg";
44
import FastImageComponent, { Source } from "@d11/react-native-fast-image";
55
import { extractStyles } from "@mendix/pluggable-widgets-tools";
@@ -64,11 +64,19 @@ export const ImageIconSVG: FunctionComponent<ImageIconSVGProps> = props => {
6464
);
6565

6666
if (image && (type === "staticImage" || type === "dynamicImage")) {
67+
// FastImage's Glide/OkHttp client on Android can be initialized before the app wires up
68+
// its cookie-decrypting network interceptor, causing remote (online document) images to
69+
// fail to load with 401s. RN's own Image component always picks up the interceptor, so
70+
// fall back to it for remote urls on Android.
71+
const uri = typeof image === "object" ? (image as ImageURISource)?.uri : undefined;
72+
const useFallback = Platform.OS === "android" && typeof uri === "string" && /^https?:\/\//i.test(uri);
73+
6774
return (
6875
<FastImageComponent
6976
testID={`${name}$Image`} // Broken because of https://github.com/DylanVann/react-native-fast-image/issues/221
7077
source={image as Source | number}
7178
resizeMode={resizeMode || "contain"}
79+
fallback={useFallback}
7280
style={[
7381
initialDimensions?.aspectRatio ? { aspectRatio: +initialDimensions.aspectRatio?.toFixed(2) } : {},
7482
width && height ? { width, height } : {},

packages/pluggableWidgets/image-native/src/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="Image" version="3.1.1" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="Image" version="3.1.2" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="Image.xml" />
66
</widgetFiles>

packages/pluggableWidgets/image-native/src/utils/imageUtils.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ function getBundledAssetSource(value: NativeImage | Readonly<ImageURISource | st
3131
return undefined;
3232
}
3333

34+
function toAndroidUri(uri: string): string {
35+
// Online images are served by the Mendix runtime as http(s) URLs and must be requested as
36+
// such so cookies are attached; only local file paths need the file:/// scheme prepended.
37+
return Platform.OS === "android" && !/^https?:\/\//i.test(uri) ? `file:///${uri}` : uri;
38+
}
39+
3440
export async function convertImageProps(
3541
datasource: DatasourceEnum,
3642
imageIcon: DynamicValue<NativeIcon> | undefined,
@@ -74,14 +80,14 @@ export async function convertImageProps(
7480
} else if (typeof imageValue === "object" && imageValue?.uri && imageValue?.name?.endsWith(".svg")) {
7581
return {
7682
type: "dynamicSVG", // Dynamic image SVG
77-
image: (Platform.OS === "android" ? "file:///" : "") + imageValue.uri
83+
image: toAndroidUri(imageValue.uri as string)
7884
};
7985
} else if (typeof imageValue === "object" && imageValue?.uri) {
8086
return {
8187
type: "dynamicImage", // Dynamic image
8288
image: {
8389
...imageValue,
84-
uri: (Platform.OS === "android" ? "file:///" : "") + imageValue.uri
90+
uri: toAndroidUri(imageValue.uri as string)
8591
}
8692
};
8793
}

0 commit comments

Comments
 (0)