feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,2 @@
import { requireNativeModule } from 'expo-modules-core';
export default requireNativeModule('ExponentImagePicker');

View File

@@ -0,0 +1,159 @@
import { PermissionResponse, PermissionStatus, Platform } from 'expo-modules-core';
import {
ImagePickerAsset,
ImagePickerResult,
MediaTypeOptions,
OpenFileBrowserOptions,
} from './ImagePicker.types';
const MediaTypeInput = {
[MediaTypeOptions.All]: 'video/mp4,video/quicktime,video/x-m4v,video/*,image/*',
[MediaTypeOptions.Images]: 'image/*',
[MediaTypeOptions.Videos]: 'video/mp4,video/quicktime,video/x-m4v,video/*',
};
export default {
async launchImageLibraryAsync({
mediaTypes = MediaTypeOptions.Images,
allowsMultipleSelection = false,
base64 = false,
}): Promise<ImagePickerResult> {
// SSR guard
if (!Platform.isDOMAvailable) {
return { canceled: true, assets: null };
}
return await openFileBrowserAsync({
mediaTypes,
allowsMultipleSelection,
base64,
});
},
async launchCameraAsync({
mediaTypes = MediaTypeOptions.Images,
allowsMultipleSelection = false,
base64 = false,
}): Promise<ImagePickerResult> {
// SSR guard
if (!Platform.isDOMAvailable) {
return { canceled: true, assets: null };
}
return await openFileBrowserAsync({
mediaTypes,
allowsMultipleSelection,
capture: true,
base64,
});
},
/*
* Delegate to expo-permissions to request camera permissions
*/
async getCameraPermissionsAsync() {
return permissionGrantedResponse();
},
async requestCameraPermissionsAsync() {
return permissionGrantedResponse();
},
/*
* Camera roll permissions don't need to be requested on web, so we always
* respond with granted.
*/
async getMediaLibraryPermissionsAsync(_writeOnly: boolean) {
return permissionGrantedResponse();
},
async requestMediaLibraryPermissionsAsync(_writeOnly: boolean): Promise<PermissionResponse> {
return permissionGrantedResponse();
},
};
function permissionGrantedResponse(): PermissionResponse {
return {
status: PermissionStatus.GRANTED,
expires: 'never',
granted: true,
canAskAgain: true,
};
}
function openFileBrowserAsync({
mediaTypes,
capture = false,
allowsMultipleSelection = false,
base64,
}: OpenFileBrowserOptions): Promise<ImagePickerResult> {
const mediaTypeFormat = MediaTypeInput[mediaTypes];
const input = document.createElement('input');
input.style.display = 'none';
input.setAttribute('type', 'file');
input.setAttribute('accept', mediaTypeFormat);
input.setAttribute('id', String(Math.random()));
if (allowsMultipleSelection) {
input.setAttribute('multiple', 'multiple');
}
if (capture) {
input.setAttribute('capture', 'camera');
}
document.body.appendChild(input);
return new Promise((resolve) => {
input.addEventListener('change', async () => {
if (input.files) {
const files = allowsMultipleSelection ? input.files : [input.files[0]];
const assets: ImagePickerAsset[] = await Promise.all(
Array.from(files).map((file) => readFile(file, { base64 }))
);
resolve({ canceled: false, assets });
} else {
resolve({ canceled: true, assets: null });
}
document.body.removeChild(input);
});
const event = new MouseEvent('click');
input.dispatchEvent(event);
});
}
function readFile(targetFile: File, options: { base64: boolean }): Promise<ImagePickerAsset> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = () => {
reject(new Error(`Failed to read the selected media because the operation failed.`));
};
reader.onload = ({ target }) => {
const uri = (target as any).result;
const returnRaw = () => resolve({ uri, width: 0, height: 0 });
if (typeof uri === 'string') {
const image = new Image();
image.src = uri;
image.onload = () => {
resolve({
uri,
width: image.naturalWidth ?? image.width,
height: image.naturalHeight ?? image.height,
mimeType: targetFile.type,
fileName: targetFile.name,
// The blob's result cannot be directly decoded as Base64 without
// first removing the Data-URL declaration preceding the
// Base64-encoded data. To retrieve only the Base64 encoded string,
// first remove data:*/*;base64, from the result.
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
...(options.base64 && { base64: uri.substr(uri.indexOf(',') + 1) }),
});
};
image.onerror = () => returnRaw();
} else {
returnRaw();
}
};
reader.readAsDataURL(targetFile);
});
}

View File

@@ -0,0 +1,215 @@
import {
PermissionStatus,
PermissionExpiration,
PermissionHookOptions,
PermissionResponse,
createPermissionHook,
UnavailabilityError,
CodedError,
} from 'expo-modules-core';
import ExponentImagePicker from './ExponentImagePicker';
import {
CameraPermissionResponse,
MediaLibraryPermissionResponse,
ImagePickerResult,
ImagePickerErrorResult,
ImagePickerOptions,
} from './ImagePicker.types';
function validateOptions(options: ImagePickerOptions) {
const { aspect, quality, videoMaxDuration } = options;
if (aspect != null) {
const [x, y] = aspect;
if (x <= 0 || y <= 0) {
throw new CodedError(
'ERR_INVALID_ARGUMENT',
`Invalid aspect ratio values ${x}:${y}. Provide positive numbers.`
);
}
}
if (quality && (quality < 0 || quality > 1)) {
throw new CodedError(
'ERR_INVALID_ARGUMENT',
`Invalid 'quality' value ${quality}. Provide a value between 0 and 1.`
);
}
if (videoMaxDuration && videoMaxDuration < 0) {
throw new CodedError(
'ERR_INVALID_ARGUMENT',
`Invalid 'videoMaxDuration' value ${videoMaxDuration}. Provide a non-negative number.`
);
}
return options;
}
// @needsAudit
/**
* Checks user's permissions for accessing camera.
* @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerapermissionresponse).
*/
export async function getCameraPermissionsAsync(): Promise<CameraPermissionResponse> {
return ExponentImagePicker.getCameraPermissionsAsync();
}
// @needsAudit
/**
* Checks user's permissions for accessing photos.
* @param writeOnly Whether to request write or read and write permissions. Defaults to `false`
* @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).
*/
export async function getMediaLibraryPermissionsAsync(
writeOnly: boolean = false
): Promise<MediaLibraryPermissionResponse> {
return ExponentImagePicker.getMediaLibraryPermissionsAsync(writeOnly);
}
// @needsAudit
/**
* Asks the user to grant permissions for accessing camera. This does nothing on web because the
* browser camera is not used.
* @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerarollpermissionresponse).
*/
export async function requestCameraPermissionsAsync(): Promise<CameraPermissionResponse> {
return ExponentImagePicker.requestCameraPermissionsAsync();
}
// @needsAudit
/**
* Asks the user to grant permissions for accessing user's photo. This method does nothing on web.
* @param writeOnly Whether to request write or read and write permissions. Defaults to `false`
* @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).
*/
export async function requestMediaLibraryPermissionsAsync(
writeOnly: boolean = false
): Promise<MediaLibraryPermissionResponse> {
const imagePickerMethod = ExponentImagePicker.requestMediaLibraryPermissionsAsync;
return imagePickerMethod(writeOnly);
}
// @needsAudit
/**
* Check or request permissions to access the media library.
* This uses both `requestMediaLibraryPermissionsAsync` and `getMediaLibraryPermissionsAsync` to interact with the permissions.
*
* @example
* ```ts
* const [status, requestPermission] = ImagePicker.useMediaLibraryPermissions();
* ```
*/
export const useMediaLibraryPermissions = createPermissionHook<
MediaLibraryPermissionResponse,
{ writeOnly?: boolean }
>({
// TODO(cedric): permission requesters should have an options param or a different requester
getMethod: (options) => getMediaLibraryPermissionsAsync(options?.writeOnly),
requestMethod: (options) => requestMediaLibraryPermissionsAsync(options?.writeOnly),
});
// @needsAudit
/**
* Check or request permissions to access the camera.
* This uses both `requestCameraPermissionsAsync` and `getCameraPermissionsAsync` to interact with the permissions.
*
* @example
* ```ts
* const [status, requestPermission] = ImagePicker.useCameraPermissions();
* ```
*/
export const useCameraPermissions = createPermissionHook({
getMethod: getCameraPermissionsAsync,
requestMethod: requestCameraPermissionsAsync,
});
// @needsAudit
/**
* Android system sometimes kills the `MainActivity` after the `ImagePicker` finishes. When this
* happens, we lost the data selected from the `ImagePicker`. However, you can retrieve the lost
* data by calling `getPendingResultAsync`. You can test this functionality by turning on
* `Don't keep activities` in the developer options.
* @return
* - **On Android:** a promise that resolves to an array of objects of exactly same type as in
* `ImagePicker.launchImageLibraryAsync` or `ImagePicker.launchCameraAsync` if the `ImagePicker`
* finished successfully. Otherwise, to the array of [`ImagePickerErrorResult`](#imagepickerimagepickererrorresult).
* - **On other platforms:** an empty array.
*/
export async function getPendingResultAsync(): Promise<
(ImagePickerResult | ImagePickerErrorResult)[]
> {
if (ExponentImagePicker.getPendingResultAsync) {
return ExponentImagePicker.getPendingResultAsync();
}
return [];
}
// @needsAudit
/**
* Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.
* On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be
* called immediately in a user interaction like a button press, otherwise the browser will block
* the request without a warning.
* > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).
* > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
* Therefore, calling `launchCameraAsync` in `componentDidMount`, for example, will **not** work as
* intended. The `cancelled` event will not be returned in the browser due to platform restrictions
* and inconsistencies across browsers.
* @param options An `ImagePickerOptions` object.
* @return A promise that resolves to an object with `canceled` and `assets` fields.
* When the user canceled the action the `assets` is always `null`, otherwise it's an array of
* the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).
*/
export async function launchCameraAsync(
options: ImagePickerOptions = {}
): Promise<ImagePickerResult> {
if (!ExponentImagePicker.launchCameraAsync) {
throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');
}
return await ExponentImagePicker.launchCameraAsync(validateOptions(options));
}
// @needsAudit
/**
* Display the system UI for choosing an image or a video from the phone's library.
* Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called
* immediately in a user interaction like a button press, otherwise the browser will block the
* request without a warning.
*
* **Animated GIFs support:** On Android, if the selected image is an animated GIF, the result image will be an
* animated GIF too if and only if `quality` is explicitly set to `1.0` and `allowsEditing` is set to `false`.
* Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the
* result (on Android the result will be a PNG). On iOS, both quality and cropping are supported.
*
* > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
* Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**
* work as intended. The `cancelled` event will not be returned in the browser due to platform
* restrictions and inconsistencies across browsers.
* @param options An object extended by [`ImagePickerOptions`](#imagepickeroptions).
* @return A promise that resolves to an object with `canceled` and `assets` fields.
* When the user canceled the action the `assets` is always `null`, otherwise it's an array of
* the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).
*/
export async function launchImageLibraryAsync(
options?: ImagePickerOptions
): Promise<ImagePickerResult> {
if (!ExponentImagePicker.launchImageLibraryAsync) {
throw new UnavailabilityError('ImagePicker', 'launchImageLibraryAsync');
}
if (options?.allowsEditing && options.allowsMultipleSelection) {
console.warn(
'[expo-image-picker] `allowsEditing` is not supported when `allowsMultipleSelection` is enabled and will be ignored.' +
"Disable either 'allowsEditing' or 'allowsMultipleSelection' in 'launchImageLibraryAsync' " +
'to fix this warning.'
);
}
return await ExponentImagePicker.launchImageLibraryAsync(options ?? {});
}
export * from './ImagePicker.types';
export type { PermissionExpiration, PermissionHookOptions, PermissionResponse };
export { PermissionStatus };

View File

@@ -0,0 +1,513 @@
import { PermissionResponse } from 'expo-modules-core';
// @needsAudit
/**
* Alias for `PermissionResponse` type exported by `expo-modules-core`.
*/
export type CameraPermissionResponse = PermissionResponse;
// @needsAudit
/**
* Extends `PermissionResponse` type exported by `expo-modules-core`, containing additional iOS-specific field.
*/
export type MediaLibraryPermissionResponse = PermissionResponse & {
/**
* Indicates if your app has access to the whole or only part of the photo library. Possible values are:
* - `'all'` if the user granted your app access to the whole photo library
* - `'limited'` if the user granted your app access only to selected photos (only available on Android API 34+ and iOS 14.0+)
* - `'none'` if user denied or hasn't yet granted the permission
*/
accessPrivileges?: 'all' | 'limited' | 'none';
};
// @needsAudit
export enum MediaTypeOptions {
/**
* Images and videos.
*/
All = 'All',
/**
* Only videos.
*/
Videos = 'Videos',
/**
* Only images.
*/
Images = 'Images',
}
// @needsAudit
export enum VideoExportPreset {
/**
* Resolution: __Unchanged__ •
* Video compression: __None__ •
* Audio compression: __None__
*/
Passthrough = 0,
/**
* Resolution: __Depends on the device__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
LowQuality = 1,
/**
* Resolution: __Depends on the device__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
MediumQuality = 2,
/**
* Resolution: __Depends on the device__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
HighestQuality = 3,
/**
* Resolution: __640 × 480__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
H264_640x480 = 4,
/**
* Resolution: __960 × 540__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
H264_960x540 = 5,
/**
* Resolution: __1280 × 720__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
H264_1280x720 = 6,
/**
* Resolution: __1920 × 1080__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
H264_1920x1080 = 7,
/**
* Resolution: __3840 × 2160__ •
* Video compression: __H.264__ •
* Audio compression: __AAC__
*/
H264_3840x2160 = 8,
/**
* Resolution: __1920 × 1080__ •
* Video compression: __HEVC__ •
* Audio compression: __AAC__
*/
HEVC_1920x1080 = 9,
/**
* Resolution: __3840 × 2160__ •
* Video compression: __HEVC__ •
* Audio compression: __AAC__
*/
HEVC_3840x2160 = 10,
}
// @needsAudit
export enum UIImagePickerControllerQualityType {
/**
* Highest available resolution.
*/
High = 0,
/**
* Depends on the device.
*/
Medium = 1,
/**
* Depends on the device.
*/
Low = 2,
/**
* 640 × 480
*/
VGA640x480 = 3,
/**
* 1280 × 720
*/
IFrame1280x720 = 4,
/**
* 960 × 540
*/
IFrame960x540 = 5,
}
/**
* Picker presentation style. Its values are directly mapped to the [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).
*
* @platform ios
*/
export enum UIImagePickerPresentationStyle {
/**
* A presentation style in which the presented picker covers the screen.
*/
FULL_SCREEN = 'fullScreen',
/**
* A presentation style that partially covers the underlying content.
*/
PAGE_SHEET = 'pageSheet',
/**
* A presentation style that displays the picker centered in the screen.
*/
FORM_SHEET = 'formSheet',
/**
* A presentation style where the picker is displayed over the app's content.
*/
CURRENT_CONTEXT = 'currentContext',
/**
* A presentation style in which the picker view covers the screen.
*/
OVER_FULL_SCREEN = 'overFullScreen',
/**
* A presentation style where the picker is displayed over the app's content.
*/
OVER_CURRENT_CONTEXT = 'overCurrentContext',
/**
* A presentation style where the picker is displayed in a popover view.
*/
POPOVER = 'popover',
/**
* The default presentation style chosen by the system.
* On older iOS versions, falls back to `WebBrowserPresentationStyle.FullScreen`.
*
* @platform ios
*/
AUTOMATIC = 'automatic',
}
/**
* Picker preferred asset representation mode. Its values are directly mapped to the [`PHPickerConfigurationAssetRepresentationMode`](https://developer.apple.com/documentation/photokit/phpickerconfigurationassetrepresentationmode).
*
* @platform ios
*/
export enum UIImagePickerPreferredAssetRepresentationMode {
/**
* A mode that indicates that the system chooses the appropriate asset representation.
*/
Automatic = 'automatic',
/**
* A mode that uses the most compatible asset representation.
*/
Compatible = 'compatible',
/**
* A mode that uses the current representation to avoid transcoding, if possible.
*/
Current = 'current',
}
export enum CameraType {
/**
* Back/rear camera.
*/
back = 'back',
/**
* Front camera
*/
front = 'front',
}
/**
* @hidden
* @deprecated Use `ImagePickerAsset` instead
*/
export type ImageInfo = ImagePickerAsset;
/**
* Represents an asset (image or video) returned by the image picker or camera.
*/
export type ImagePickerAsset = {
/**
* URI to the local image or video file (usable as the source of an `Image` element, in the case of
* an image) and `width` and `height` specify the dimensions of the media.
*/
uri: string;
/**
* The unique ID that represents the picked image or video, if picked from the library. It can be used
* by [expo-media-library](./media-library) to manage the picked asset.
*
* > This might be `null` when the ID is unavailable or the user gave limited permission to access the media library.
* > On Android, the ID is unavailable when the user selects a photo by directly browsing file system.
*
* @platform ios
* @platform android
*/
assetId?: string | null;
/**
* Width of the image or video.
*/
width: number;
/**
* Height of the image or video.
*/
height: number;
/**
* The type of the asset.
*/
type?: 'image' | 'video';
/**
* Preferred filename to use when saving this item. This might be `null` when the name is unavailable
* or user gave limited permission to access the media library.
*
*/
fileName?: string | null;
/**
* File size of the picked image or video, in bytes.
*
*/
fileSize?: number;
/**
* The `exif` field is included if the `exif` option is truthy, and is an object containing the
* image's EXIF data. The names of this object's properties are EXIF tags and the values are the
* respective EXIF values for those tags.
*/
exif?: Record<string, any> | null;
/**
* When the `base64` option is truthy, it is a Base64-encoded string of the selected image's JPEG data, otherwise `null`.
* If you prepend this with `'data:image/jpeg;base64,'` to create a data URI,
* you can use it as the source of an `Image` element; for example:
* ```ts
* <Image
* source={{ uri: 'data:image/jpeg;base64,' + asset.base64 }}
* style={{ width: 200, height: 200 }}
* />
* ```
*/
base64?: string | null;
/**
* Length of the video in milliseconds or `null` if the asset is not a video.
*/
duration?: number | null;
/**
* The MIME type of the selected asset or `null` if could not be determined.
*/
mimeType?: string;
};
// @needsAudit
export type ImagePickerErrorResult = {
/**
* The error code.
*/
code: string;
/**
* The error message.
*/
message: string;
/**
* The exception which caused the error.
*/
exception?: string;
};
// @needsAudit
/**
* Type representing successful and canceled pick result.
*/
export type ImagePickerResult = ImagePickerSuccessResult | ImagePickerCanceledResult;
/**
* Type representing successful pick result.
*/
export type ImagePickerSuccessResult = {
/**
* Boolean flag set to `false` showing that the request was successful.
*/
canceled: false;
/**
* An array of picked assets.
*/
assets: ImagePickerAsset[];
};
/**
* Type representing canceled pick result.
*/
export type ImagePickerCanceledResult = {
/**
* Boolean flag set to `true` showing that the request was canceled.
*/
canceled: true;
/**
* `null` signifying that the request was canceled.
*/
assets: null;
};
/**
* @hidden
* @deprecated Use `ImagePickerResult` instead.
*/
export type ImagePickerCancelledResult = ImagePickerCanceledResult;
/**
* @hidden
* @deprecated `ImagePickerMultipleResult` has been deprecated in favor of `ImagePickerResult`.
*/
export type ImagePickerMultipleResult = ImagePickerResult;
// @needsAudit
export type ImagePickerOptions = {
/**
* Whether to show a UI to edit the image after it is picked. On Android the user can crop and
* rotate the image and on iOS simply crop it.
*
* > - Cropping multiple images is not supported - this option is mutually exclusive with `allowsMultipleSelection`.
* > - On iOS, this option is ignored if `allowsMultipleSelection` is enabled.
* > - On iOS cropping a `.bmp` image will convert it to `.png`.
*
* @default false
* @platform ios
* @platform android
*/
allowsEditing?: boolean;
/**
* An array with two entries `[x, y]` specifying the aspect ratio to maintain if the user is
* allowed to edit the image (by passing `allowsEditing: true`). This is only applicable on
* Android, since on iOS the crop rectangle is always a square.
*/
aspect?: [number, number];
/**
* Specify the quality of compression, from `0` to `1`. `0` means compress for small size,
* `1` means compress for maximum quality.
* > Note: If the selected image has been compressed before, the size of the output file may be
* > bigger than the size of the original image.
*
* > Note: On iOS, if a `.bmp` or `.png` image is selected from the library, this option is ignored.
*
* @default 0.2
* @platform ios
* @platform android
*/
quality?: number;
/**
* Choose what type of media to pick.
* @default ImagePicker.MediaTypeOptions.Images
*/
mediaTypes?: MediaTypeOptions;
/**
* Whether to also include the EXIF data for the image. On iOS the EXIF data does not include GPS
* tags in the camera case.
*/
exif?: boolean;
/**
* Whether to also include the image data in Base64 format.
*/
base64?: boolean;
/**
* Specify preset which will be used to compress selected video.
* @default ImagePicker.VideoExportPreset.Passthrough
* @platform ios 11+
* @deprecated See [`videoExportPreset`](https://developer.apple.com/documentation/uikit/uiimagepickercontroller/2890964-videoexportpreset?language=objc)
* in Apple documentation.
*/
videoExportPreset?: VideoExportPreset;
/**
* Specify the quality of recorded videos. Defaults to the highest quality available for the device.
* @default ImagePicker.UIImagePickerControllerQualityType.High
* @platform ios
*/
videoQuality?: UIImagePickerControllerQualityType;
/**
* Whether or not to allow selecting multiple media files at once.
*
* > Cropping multiple images is not supported - this option is mutually exclusive with `allowsEditing`.
* > If this option is enabled, then `allowsEditing` is ignored.
*
* @default false
* @platform ios 14+
* @platform android
* @platform web
*/
allowsMultipleSelection?: boolean;
/**
* The maximum number of items that user can select. Applicable when `allowsMultipleSelection` is enabled.
* Setting the value to `0` sets the selection limit to the maximum that the system supports.
*
* @platform ios 14+
* @platform android
* @default 0
*/
selectionLimit?: number;
/**
* Whether to display number badges when assets are selected. The badges are numbered
* in selection order. Assets are then returned in the exact same order they were selected.
*
* > Assets should be returned in the selection order regardless of this option,
* > but there is no guarantee that it is always true when this option is disabled.
*
* @platform ios 15+
* @default false
*/
orderedSelection?: boolean;
/**
* Maximum duration, in seconds, for video recording. Setting this to `0` disables the limit.
* Defaults to `0` (no limit).
* - **On iOS**, when `allowsEditing` is set to `true`, maximum duration is limited to 10 minutes.
* This limit is applied automatically, if `0` or no value is specified.
* - **On Android**, effect of this option depends on support of installed camera app.
* - **On Web** this option has no effect - the limit is browser-dependant.
*/
videoMaxDuration?: number;
/**
* Choose [presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle?language=objc)
* to customize view during taking photo/video.
* @default ImagePicker.UIImagePickerPresentationStyle.Automatic
* @platform ios
*/
presentationStyle?: UIImagePickerPresentationStyle;
/**
* Selects the camera-facing type. The `CameraType` enum provides two options:
* `front` for the front-facing camera and `back` for the back-facing camera.
* - **On Android**, the behavior of this option may vary based on the camera app installed on the device.
* @default CameraType.back
* @platform ios
* @platform android
*/
cameraType?: CameraType;
/**
* Choose [preferred asset representation mode](https://developer.apple.com/documentation/photokit/phpickerconfigurationassetrepresentationmode)
* to use when loading assets.
* @default ImagePicker.UIImagePickerPreferredAssetRepresentationMode.Automatic
* @platform ios 14+
*/
preferredAssetRepresentationMode?: UIImagePickerPreferredAssetRepresentationMode;
/**
* Uses the legacy image picker on Android. This will allow media to be selected from outside the users photo library.
* @platform android
* @default false
*/
legacy?: boolean;
};
// @needsAudit
export type OpenFileBrowserOptions = {
/**
* Choose what type of media to pick.
* @default ImagePicker.MediaTypeOptions.Images
*/
mediaTypes: MediaTypeOptions;
// @docsMissing
capture?: boolean;
/**
* Whether or not to allow selecting multiple media files at once.
* @platform web
*/
allowsMultipleSelection: boolean;
/**
* Whether to also include the image data in Base64 format.
*/
base64: boolean;
};
/**
* @hidden
* @deprecated Use `ImagePickerResult` or `OpenFileBrowserOptions` instead.
*/
export type ExpandImagePickerResult<T extends ImagePickerOptions | OpenFileBrowserOptions> =
T extends {
allowsMultipleSelection: true;
}
? ImagePickerResult
: ImagePickerResult;