diff --git a/packages/jsActions/mobile-resources-native/CHANGELOG.md b/packages/jsActions/mobile-resources-native/CHANGELOG.md index fa0ea1150..f5f525aa3 100644 --- a/packages/jsActions/mobile-resources-native/CHANGELOG.md +++ b/packages/jsActions/mobile-resources-native/CHANGELOG.md @@ -6,7 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- We refactored the GetDeviceInfo action to use the shared device metrics utility instead of directly importing react-native-device-info. + ## [12.6.0] Native Mobile Resources - 2026-11-4 + ### Fixed - Fixed an issue where actions using decimal values threw a "Big is not defined" error at runtime. @@ -17,11 +22,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We bumped the `brace-expansion` package to v5.0.12 to fix a denial-of-service vulnerability. ## [5.3.5] BottomSheet + ### Fixed - Fixed the app becoming unresponsive to touches after a modal bottom sheet (basic or custom rendering) was closed while it was still opening, e.g. when opening and closing it quickly. A sheet whose trigger attribute is set to false while it is opening now also closes once it has opened. ## [2.4.1] ColorPicker + ### Added - We added screen reader label properties for the hue, saturation, brightness and transparency sliders. @@ -36,16 +43,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We fixed the other sliders moving slightly while one slider is dragged, and hue and saturation being lost when the color becomes black or white. ## [3.2.0] Image + ### Changed - The enlarged image is now presented through the shared MxOverlay component. Screen readers now treat it as a modal, move focus to the enlarged image when it opens and return focus to the image when it closes. ## [4.6.0] IntroScreen + ### Changed - The IntroScreen is now presented through the shared MxOverlay component. Its appearance and behaviour, including blocking the Android back button, are unchanged. ## [4.5.0] ProgressBar + ### Changed - Removed the react-native-progress dependency. There is no change in appearance or behavior. @@ -55,6 +65,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - The progress bar now announces itself to screen readers with a progress bar role and its current value. ## [3.4.0] ProgressCircle + ### Changed - Removed the react-native-progress dependency. There is no change in appearance or behavior. @@ -64,6 +75,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - The progress circle now announces itself to screen readers with a progress bar role and its current value. ## [3.1.0] RangeSlider + ### Changed - We replaced @miblanchard/react-native-slider with a built-in slider component. @@ -73,11 +85,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We fixed the range slider thumbs not announcing their values or being adjustable with VoiceOver and TalkBack. ## [3.1.0] Signature + ### Added - We added mendix-native as a dependency to provide native file system. ## [4.1.0] Slider + ### Changed - We replaced @miblanchard/react-native-slider with a built-in slider component. @@ -87,6 +101,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We fixed the slider not announcing its value or being adjustable with VoiceOver and TalkBack. ## [6.5.0] VideoPlayer + ### Added - We added mendix-native as a dependency to provide platform utilities. diff --git a/packages/jsActions/mobile-resources-native/package.json b/packages/jsActions/mobile-resources-native/package.json index 7be642c95..933c712fa 100644 --- a/packages/jsActions/mobile-resources-native/package.json +++ b/packages/jsActions/mobile-resources-native/package.json @@ -28,6 +28,7 @@ "release:marketplace": "node ../../../scripts/release/marketplaceRelease.js" }, "dependencies": { + "@mendix/piw-native-utils-internal": "*", "@react-native-camera-roll/camera-roll": "7.10.2", "react-native-notify-kit": "10.4.7", "@react-native-firebase/messaging": "20.1.0", @@ -37,7 +38,6 @@ "mendix-native": "https://github.com/mendix/mendix-native/releases/download/v0.5.2/mendix-native-v0.5.2.tgz", "mime": "3.0.0", "react-native-blob-util": "0.24.7", - "react-native-device-info": "15.0.2", "react-native-file-viewer-turbo": "0.7.4", "react-native-image-picker": "7.2.3", "react-native-localize": "3.7.0", @@ -52,4 +52,4 @@ "mendix": "11.8.0", "rimraf": "^6.1.2" } -} \ No newline at end of file +} diff --git a/packages/jsActions/mobile-resources-native/src/platform/GetDeviceInfo.ts b/packages/jsActions/mobile-resources-native/src/platform/GetDeviceInfo.ts index b633eade7..89d6a8b5f 100644 --- a/packages/jsActions/mobile-resources-native/src/platform/GetDeviceInfo.ts +++ b/packages/jsActions/mobile-resources-native/src/platform/GetDeviceInfo.ts @@ -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 DeviceInfo from "react-native-device-info"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; import { getLocales, getCountry, getTimeZone, uses24HourClock } from "react-native-localize"; // BEGIN EXTRA CODE @@ -17,20 +17,21 @@ import { getLocales, getCountry, getTimeZone, uses24HourClock } from "react-nati */ export async function GetDeviceInfo(): Promise { // BEGIN USER CODE + const deviceMetrics = useDeviceMetrics(); return Promise.all([ createMxObject("NativeMobileResources.DeviceInfo"), - DeviceInfo.getBatteryLevel(), - DeviceInfo.getFontScale(), - DeviceInfo.getFreeDiskStorage(), - DeviceInfo.getTotalDiskCapacity(), - DeviceInfo.getTotalMemory(), - DeviceInfo.getCarrier(), - DeviceInfo.getManufacturer(), - DeviceInfo.getUserAgent(), - DeviceInfo.getUniqueId(), - DeviceInfo.isEmulator(), - DeviceInfo.isLandscape() + deviceMetrics.getBatteryLevel(), + deviceMetrics.getFontScale(), + deviceMetrics.getFreeDiskStorage(), + deviceMetrics.getTotalDiskCapacity(), + deviceMetrics.getTotalMemory(), + deviceMetrics.getCarrier(), + deviceMetrics.getManufacturer(), + deviceMetrics.getUserAgent(), + deviceMetrics.getUniqueId(), + deviceMetrics.isEmulator(), + deviceMetrics.isLandscape() ]).then( async ([ mxObject, @@ -47,33 +48,33 @@ export async function GetDeviceInfo(): Promise { isLandscape ]) => { const locales = getLocales(); - mxObject.set("ApplicationName", DeviceInfo.getApplicationName()); + mxObject.set("ApplicationName", deviceMetrics.getApplicationName()); mxObject.set("BatteryLevel", new Big(batteryLevel.toFixed(2))); - mxObject.set("Brand", DeviceInfo.getBrand()); - mxObject.set("BuildNumber", String(DeviceInfo.getBuildNumber())); - mxObject.set("BundleId", DeviceInfo.getBundleId()); + mxObject.set("Brand", deviceMetrics.getBrand()); + mxObject.set("BuildNumber", String(deviceMetrics.getBuildNumber())); + mxObject.set("BundleId", deviceMetrics.getBundleId()); mxObject.set("Carrier", carrier); mxObject.set("DeviceCountry", getCountry()); - mxObject.set("DeviceId", DeviceInfo.getDeviceId()); + mxObject.set("DeviceId", deviceMetrics.getDeviceId()); mxObject.set("DeviceLocale", locales && locales.length > 0 ? locales[0].languageTag : ""); mxObject.set("FontScale", new Big(fontScale.toFixed(2))); mxObject.set("FreeDiskStorage", new Big(freeDiskStorage)); mxObject.set("Manufacturer", manufacturer); - mxObject.set("Model", DeviceInfo.getModel()); - mxObject.set("ReadableVersion", DeviceInfo.getReadableVersion()); - mxObject.set("SystemName", DeviceInfo.getSystemName()); - mxObject.set("SystemVersion", DeviceInfo.getSystemVersion()); + mxObject.set("Model", deviceMetrics.getModel()); + mxObject.set("ReadableVersion", deviceMetrics.getReadableVersion()); + mxObject.set("SystemName", deviceMetrics.getSystemName()); + mxObject.set("SystemVersion", deviceMetrics.getSystemVersion()); mxObject.set("Timezone", getTimeZone()); mxObject.set("TotalDiskCapacity", new Big(totalDiskCapacity)); mxObject.set("TotalMemory", new Big(totalMemory)); mxObject.set("UniqueId", uniqueId); mxObject.set("UserAgent", userAgent); - mxObject.set("Version", DeviceInfo.getVersion()); + mxObject.set("Version", deviceMetrics.getVersion()); mxObject.set("Is24Hour", uses24HourClock()); mxObject.set("IsEmulator", isEmulator); - mxObject.set("IsTablet", DeviceInfo.isTablet()); + mxObject.set("IsTablet", deviceMetrics.isTablet); mxObject.set("IsLandscape", isLandscape); - mxObject.set("HasNotch", DeviceInfo.hasNotch()); + mxObject.set("HasNotch", deviceMetrics.hasNotch); return mxObject; } diff --git a/packages/pluggableWidgets/bottom-sheet-native/CHANGELOG.md b/packages/pluggableWidgets/bottom-sheet-native/CHANGELOG.md index 29ad35bd3..2f15f3b93 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/CHANGELOG.md +++ b/packages/pluggableWidgets/bottom-sheet-native/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- Removed unnecessary react-native-device-info dependency. + ## [5.3.5] - 2026-11-4 ### Fixed diff --git a/packages/pluggableWidgets/bottom-sheet-native/package.json b/packages/pluggableWidgets/bottom-sheet-native/package.json index 1c0ddb020..57dada8d8 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/package.json +++ b/packages/pluggableWidgets/bottom-sheet-native/package.json @@ -1,7 +1,7 @@ { "name": "bottom-sheet-native", "widgetName": "BottomSheet", - "version": "5.3.5", + "version": "5.3.6", "license": "Apache-2.0", "repository": { "type": "git", @@ -23,7 +23,6 @@ "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2", "react-native-gesture-handler": "2.31.2", "react-native-reanimated": "4.3.1", "react-native-worklets": "0.8.3" diff --git a/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx b/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx index 2e00f7a5f..a6108bbf8 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx +++ b/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx @@ -17,10 +17,6 @@ jest.mock("react-native-reanimated", () => { return Reanimated; }); -jest.mock("react-native-device-info", () => ({ - getDeviceId: () => "iPhone10,6" -})); - jest.mock("react-native/Libraries/Utilities/Platform", () => { const Platform = jest.requireActual("react-native/Libraries/Utilities/Platform"); Platform.OS = "ios"; diff --git a/packages/pluggableWidgets/bottom-sheet-native/src/package.xml b/packages/pluggableWidgets/bottom-sheet-native/src/package.xml index 7b6ff4bcd..adddc9d6c 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/src/package.xml +++ b/packages/pluggableWidgets/bottom-sheet-native/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts b/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts index 1b1db34b6..8ea337d9f 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts +++ b/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts @@ -1,5 +1,4 @@ import { TextStyle, ViewStyle } from "react-native"; -import DeviceInfo from "react-native-device-info"; export interface ModalItemsStyle { container?: ModalItemContainerStyle; @@ -19,26 +18,3 @@ export interface BottomSheetStyle { modal: ViewStyle; modalItems: ModalItemsStyle; } - -const isiPhoneModelWithNotch = (): boolean => { - const model = DeviceInfo.getDeviceId(); - if (model.indexOf("iPhone") !== -1) { - switch (model) { - case "iPhone10,6": // iPhone X GSM - case "iPhone11,2": // iPhone XS - case "iPhone11,4": // iPhone XS Max - case "iPhone11,6": // iPhone XS Max Global - case "iPhone11,8": // Iphone XR - case "iPhone12,1": // Iphone 11 - case "iPhone12,3": // Iphone 11 Pro - case "iPhone12,5": // Iphone 11 Pro Max - return true; - default: - return false; - } - } - return false; -}; - -export const defaultPaddings = { paddingBottom: isiPhoneModelWithNotch() ? 24 : 0 }; -export const defaultMargins = { marginBottom: isiPhoneModelWithNotch() ? 24 : 0 }; diff --git a/packages/pluggableWidgets/gallery-native/CHANGELOG.md b/packages/pluggableWidgets/gallery-native/CHANGELOG.md index 1f7f1c06f..4f2b89615 100644 --- a/packages/pluggableWidgets/gallery-native/CHANGELOG.md +++ b/packages/pluggableWidgets/gallery-native/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- Migrated device metrics utility to shared package for better code reusability. + ## [2.3.0] - 2026-10-8 ### Fixed diff --git a/packages/pluggableWidgets/gallery-native/package.json b/packages/pluggableWidgets/gallery-native/package.json index 282da9eb2..e4af9d401 100644 --- a/packages/pluggableWidgets/gallery-native/package.json +++ b/packages/pluggableWidgets/gallery-native/package.json @@ -1,7 +1,7 @@ { "name": "gallery-native", "widgetName": "Gallery", - "version": "2.3.0", + "version": "2.3.1", "description": "A flexible gallery widget that renders columns, rows and layouts.", "copyright": "© Mendix Technology BV 2022. All rights reserved.", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "test": "pluggable-widgets-tools test:unit:native" }, "dependencies": { + "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", - "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2" + "@shopify/flash-list": "2.3.1" }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*" diff --git a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx index a26be77a6..08f15959b 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx @@ -10,7 +10,7 @@ import { useWindowDimensions } from "react-native"; import { ObjectItem, DynamicValue } from "mendix"; -import DeviceInfo from "react-native-device-info"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; import { GalleryStyle } from "../ui/Styles"; import { HorizontalItemSizingEnum, PaginationEnum, ScrollDirectionEnum } from "../../typings/GalleryProps"; import { isAvailable } from "@mendix/piw-utils-internal"; @@ -39,8 +39,9 @@ export interface GalleryProps { } export const Gallery = (props: GalleryProps): ReactElement => { + const { isTablet } = useDeviceMetrics(); const isScrollDirectionVertical = props.scrollDirection === "vertical"; - const numColumns = DeviceInfo.isTablet() ? props.tabletColumns : props.phoneColumns; + const numColumns = isTablet ? props.tabletColumns : props.phoneColumns; const firstItemId = props.items?.[0]?.id; const lastItemId = props.items?.[props.items.length - 1]?.id; const { name, style, itemRenderer } = props; diff --git a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx index 59901208f..ea4615c1a 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx @@ -3,7 +3,9 @@ import { ObjectItem, GUID, DynamicValue, ValueStatus } from "mendix"; import { render, fireEvent, act } from "@testing-library/react-native"; import { Gallery, GalleryProps } from "../Gallery"; -jest.mock("react-native-device-info", () => ({ isTablet: jest.fn().mockReturnValue(false) })); +jest.mock("@mendix/piw-native-utils-internal", () => ({ + useDeviceMetrics: () => ({ isTablet: false }) +})); jest.mock("@shopify/flash-list"); // Mocking FlashList API because it causes issues with the test renderer const itemWrapperFunction = diff --git a/packages/pluggableWidgets/gallery-native/src/package.xml b/packages/pluggableWidgets/gallery-native/src/package.xml index 410357eca..d860d4c3c 100644 --- a/packages/pluggableWidgets/gallery-native/src/package.xml +++ b/packages/pluggableWidgets/gallery-native/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/intro-screen-native/CHANGELOG.md b/packages/pluggableWidgets/intro-screen-native/CHANGELOG.md index 958861bfb..bae46b425 100644 --- a/packages/pluggableWidgets/intro-screen-native/CHANGELOG.md +++ b/packages/pluggableWidgets/intro-screen-native/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- Migrated device metrics utility to shared package for better code reusability and removed static iPhone list for notch detection. + ## [4.6.0] - 2026-11-4 ### Changed diff --git a/packages/pluggableWidgets/intro-screen-native/package.json b/packages/pluggableWidgets/intro-screen-native/package.json index 23901140a..aa6c39e9c 100644 --- a/packages/pluggableWidgets/intro-screen-native/package.json +++ b/packages/pluggableWidgets/intro-screen-native/package.json @@ -1,7 +1,7 @@ { "name": "intro-screen-native", "widgetName": "IntroScreen", - "version": "4.6.0", + "version": "4.6.1", "license": "Apache-2.0", "repository": { "type": "git", @@ -22,8 +22,7 @@ "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", "@react-native-async-storage/async-storage": "2.2.0", - "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2" + "@shopify/flash-list": "2.3.1" }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*" diff --git a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx index 0bed8ea0e..248187806 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx @@ -7,18 +7,17 @@ import { SwipeableContainer } from "./SwipeableContainer"; import AsyncStorage from "@react-native-async-storage/async-storage"; import deepmerge from "deepmerge"; import { executeAction } from "@mendix/piw-utils-internal"; -import { MxOverlay } from "@mendix/piw-native-utils-internal"; +import { MxOverlay, useDeviceMetrics } from "@mendix/piw-native-utils-internal"; // The popup container draws its own dimmed background, so the overlay backdrop must not add a second one const transparentBackdrop = { backgroundColor: "transparent" }; export function IntroScreen(props: IntroScreenProps): JSX.Element { const [visible, setVisible] = useState(false); + const { hasNotch } = useDeviceMetrics(); + const notchStyle = hasNotch ? { paginationContainer: { bottom: 22 } } : {}; const customStyles = props.style ? props.style.filter(o => o != null) : []; - const styles = - customStyles.length > 0 - ? deepmerge.all([defaultWelcomeScreenStyle, ...customStyles]) - : defaultWelcomeScreenStyle; + const styles = deepmerge.all([defaultWelcomeScreenStyle, notchStyle, ...customStyles]); useEffect(() => { let isMounted = true; diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx index 4c10dee4d..99476f296 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx @@ -6,10 +6,13 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn().mockReturnValue(true), - getDeviceId: jest.fn().mockReturnValue("iPhone10,6") -})); +jest.mock("@mendix/piw-native-utils-internal", () => { + const actual = jest.requireActual("@mendix/piw-native-utils-internal"); + return { + ...actual, + useDeviceMetrics: () => ({ hasNotch: true }) + }; +}); jest.mock("@react-native-async-storage/async-storage", () => ({ getItem: jest.fn().mockResolvedValue(null), diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx index 123875a91..5757b2370 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx @@ -6,10 +6,13 @@ import { Modal, View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn(), - getDeviceId: jest.fn().mockReturnValue("") -})); +jest.mock("@mendix/piw-native-utils-internal", () => { + const actual = jest.requireActual("@mendix/piw-native-utils-internal"); + return { + ...actual, + useDeviceMetrics: () => ({ hasNotch: false }) + }; +}); jest.mock("@react-native-async-storage/async-storage", () => ({ getItem: jest.fn().mockResolvedValue(null), @@ -41,11 +44,6 @@ describe("Intro Screen", () => { hideIndicatorLastSlide: false, identifier: "" }; - - jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn(), - getDeviceId: jest.fn().mockReturnValue("iPhone") - })); }); it("renders", () => { diff --git a/packages/pluggableWidgets/intro-screen-native/src/package.xml b/packages/pluggableWidgets/intro-screen-native/src/package.xml index e0e227e8b..fe26f3fed 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/package.xml +++ b/packages/pluggableWidgets/intro-screen-native/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts b/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts index 4f81457f7..6581d3d41 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts +++ b/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts @@ -1,30 +1,8 @@ import { Style } from "@mendix/piw-native-utils-internal"; import { I18nManager, Platform, StyleSheet, TextStyle, ViewStyle } from "react-native"; import absoluteFillObject = StyleSheet.absoluteFillObject; -import DeviceInfo from "react-native-device-info"; - -const isiPhoneModelWithNotch = (): boolean => { - const model = DeviceInfo.getDeviceId(); - if (model.indexOf("iPhone") !== -1) { - switch (model) { - case "iPhone10,6": // iPhone X GSM - case "iPhone11,2": // iPhone XS - case "iPhone11,4": // iPhone XS Max - case "iPhone11,6": // iPhone XS Max Global - case "iPhone11,8": // Iphone XR - case "iPhone12,1": // Iphone 11 - case "iPhone12,3": // Iphone 11 Pro - case "iPhone12,5": // Iphone 11 Pro Max - return true; - default: - return false; - } - } - return false; -}; const isAndroidRTL = I18nManager.isRTL && Platform.OS === "android"; -const isIphoneWithNotch = DeviceInfo.hasNotch() || (Platform.OS === "ios" && isiPhoneModelWithNotch()); const defaultButtonBetweenContainer: ViewStyle = { flexDirection: "row", @@ -91,7 +69,7 @@ export const defaultWelcomeScreenStyle: IntroScreenStyle = { }, paginationContainer: { position: "absolute", - bottom: isIphoneWithNotch ? 22 : 0, + bottom: 0, left: 0, right: 0, justifyContent: "space-between", diff --git a/packages/tools/piw-native-utils-internal/package.json b/packages/tools/piw-native-utils-internal/package.json index a8b4636aa..01450a0d7 100644 --- a/packages/tools/piw-native-utils-internal/package.json +++ b/packages/tools/piw-native-utils-internal/package.json @@ -24,6 +24,9 @@ "prepare": "pnpm build", "postinstall": "pnpm build" }, + "dependencies": { + "react-native-device-info": "15.0.2" + }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*", "rimraf": "^6.1.2" diff --git a/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts new file mode 100644 index 000000000..f436bbaef --- /dev/null +++ b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts @@ -0,0 +1,63 @@ +export interface DeviceMetrics { + // Sync properties (used by widgets) + hasNotch: boolean; + isTablet: boolean; + // Async methods (used by JS Actions) + getBatteryLevel: () => Promise; + getFontScale: () => Promise; + getFreeDiskStorage: () => Promise; + getTotalDiskCapacity: () => Promise; + getTotalMemory: () => Promise; + getCarrier: () => Promise; + getManufacturer: () => Promise; + getUserAgent: () => Promise; + getUniqueId: () => Promise; + isEmulator: () => Promise; + isLandscape: () => Promise; + // Sync methods (used by JS Actions) + getApplicationName: () => string; + getBrand: () => string; + getBuildNumber: () => string | number; + getBundleId: () => string; + getDeviceId: () => string; + getModel: () => string; + getReadableVersion: () => string; + getSystemName: () => string; + getSystemVersion: () => string; + getVersion: () => string; +} + +export function useDeviceMetrics(): DeviceMetrics { + // Lazy import - only loads react-native-device-info when this function is called + // This prevents the module from being bundled during widget build + const DeviceInfo = require("react-native-device-info").default; + + return { + // Sync properties for immediate use (widgets) + hasNotch: DeviceInfo.hasNotch(), + isTablet: DeviceInfo.isTablet(), + // Async methods - returned as functions for lazy evaluation + getBatteryLevel: () => DeviceInfo.getBatteryLevel(), + getFontScale: () => DeviceInfo.getFontScale(), + getFreeDiskStorage: () => DeviceInfo.getFreeDiskStorage(), + getTotalDiskCapacity: () => DeviceInfo.getTotalDiskCapacity(), + getTotalMemory: () => DeviceInfo.getTotalMemory(), + getCarrier: () => DeviceInfo.getCarrier(), + getManufacturer: () => DeviceInfo.getManufacturer(), + getUserAgent: () => DeviceInfo.getUserAgent(), + getUniqueId: () => DeviceInfo.getUniqueId(), + isEmulator: () => DeviceInfo.isEmulator(), + isLandscape: () => DeviceInfo.isLandscape(), + // Sync methods - returned as functions for consistency + getApplicationName: () => DeviceInfo.getApplicationName(), + getBrand: () => DeviceInfo.getBrand(), + getBuildNumber: () => DeviceInfo.getBuildNumber(), + getBundleId: () => DeviceInfo.getBundleId(), + getDeviceId: () => DeviceInfo.getDeviceId(), + getModel: () => DeviceInfo.getModel(), + getReadableVersion: () => DeviceInfo.getReadableVersion(), + getSystemName: () => DeviceInfo.getSystemName(), + getSystemVersion: () => DeviceInfo.getSystemVersion(), + getVersion: () => DeviceInfo.getVersion() + }; +} diff --git a/packages/tools/piw-native-utils-internal/src/index.ts b/packages/tools/piw-native-utils-internal/src/index.ts index 5ffd78bff..b89fcf207 100644 --- a/packages/tools/piw-native-utils-internal/src/index.ts +++ b/packages/tools/piw-native-utils-internal/src/index.ts @@ -5,3 +5,4 @@ export { MxOverlay, MxOverlayProps, MxOverlayPlacement } from "./components/MxOv export { Slider, SliderProps, RangeSlider, RangeSliderProps, RangeSliderValue } from "./components/Slider"; export { Bar, BarProps } from "./components/Bar"; export { Arc, ArcProps, makeArcPath } from "./components/Arc"; +export { useDeviceMetrics, DeviceMetrics } from "./deviceMetrics"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50f58dbd9..7bf6b0c20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,6 +123,9 @@ importers: packages/jsActions/mobile-resources-native: dependencies: + '@mendix/piw-native-utils-internal': + specifier: '*' + version: link:../../tools/piw-native-utils-internal '@react-native-camera-roll/camera-roll': specifier: 7.10.2 version: 7.10.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) @@ -147,9 +150,6 @@ importers: react-native-blob-util: specifier: 0.24.7 version: 0.24.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) react-native-file-viewer-turbo: specifier: 0.7.4 version: 0.7.4(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) @@ -374,9 +374,6 @@ importers: '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) react-native-gesture-handler: specifier: 2.31.2 version: 2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) @@ -485,15 +482,15 @@ importers: packages/pluggableWidgets/gallery-native: dependencies: + '@mendix/piw-native-utils-internal': + specifier: '*' + version: link:../../tools/piw-native-utils-internal '@mendix/piw-utils-internal': specifier: '*' version: link:../../tools/piw-utils-internal '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0 @@ -554,9 +551,6 @@ importers: '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0 @@ -895,6 +889,10 @@ importers: version: 11.12.0(patch_hash=c3d32a9026cacb93268037356ae25d0e56d1b9b3ecb22aea09cbf1c69542670e)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@types/babel__core@7.20.5)(@types/node@20.19.43)(jest-util@30.4.1)(jiti@2.6.1)(picomatch@4.0.5)(react-dom@19.2.3(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(tslib@2.8.1) packages/tools/piw-native-utils-internal: + dependencies: + react-native-device-info: + specifier: 15.0.2 + version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0