export * from './specs/MultipleImagePicker.nitro' export * from './types' import { NitroModules } from 'react-native-nitro-modules' import { type MultipleImagePicker } from './specs/MultipleImagePicker.nitro' import { processColor, Appearance } from 'react-native' import { PickerResult, Config, NitroConfig, CropResult, CropConfig, NitroCropConfig, PreviewConfig, NitroPreviewConfig, MediaPreview, CameraConfig, NitroCameraConfig, CameraResult, Language, } from './types' import { CropReject, CameraError } from './types/error' const Picker = NitroModules.createHybridObject( 'MultipleImagePicker' ) type IPromisePicker = T['selectMode'] extends 'single' ? PickerResult : PickerResult[] export async function openPicker( conf: T ): Promise> { return new Promise((resolved, rejected) => { const config = { ...defaultOptions, ...conf } as NitroConfig config.primaryColor = processColor(config.primaryColor) as any config.backgroundDark = processColor(config.backgroundDark) as any if ((config as Config)?.theme === 'system') { const theme = Appearance.getColorScheme() ?? 'light' config.theme = theme } config.language = validateLanguage(config.language) if (typeof config.crop === 'boolean') { config.crop = config.crop ? { ratio: [] } : undefined } if (config.crop) config.crop.ratio = config.crop?.ratio ?? [] return Picker.openPicker( config, (result: PickerResult[]) => { resolved(result as IPromisePicker) }, (reject: number) => { rejected(reject) } ) }) } export async function openCropper( image: string, config?: CropConfig ): Promise { return new Promise( (resolved, rejected: (reason: (typeof CropReject)[0]) => void) => { const cropConfig = { presentation: 'fullScreenModal', language: 'system', ratio: [], ...config, } as NitroCropConfig cropConfig.language = validateLanguage(cropConfig.language) return Picker.openCrop( image, cropConfig, (result: CropResult) => { resolved(result) }, (error: number) => { rejected(CropReject?.[error as 0 | 1] ?? CropReject[0]) } ) } ) } export function openPreview( media: MediaPreview[] | PickerResult[], index: number = 0, conf?: PreviewConfig ): void { const config: PreviewConfig = { language: conf?.language ?? 'system', videoAutoPlay: true, ...conf, } if (config?.language && !LANGUAGES.includes(config.language)) { config.language = 'system' } if (media.length === 0) { throw new Error('Media is required') } return Picker.openPreview( media as MediaPreview[], index, config as NitroPreviewConfig, config?.onLongPress ?? (() => {}) ) } export async function openCamera(config?: CameraConfig): Promise { return new Promise((resolved, rejected) => { const cameraConfig = { cameraDevice: 'back', presentation: 'fullScreenModal', language: 'system', mediaType: 'all', allowLocation: true, isSaveSystemAlbum: false, ...config, } as NitroCameraConfig cameraConfig.color = processColor(cameraConfig.color ?? primaryColor) as any cameraConfig.language = validateLanguage(cameraConfig.language) if (typeof cameraConfig.crop === 'boolean') { cameraConfig.crop = cameraConfig.crop ? { ratio: [] } : undefined } if (cameraConfig.crop && !cameraConfig.crop?.ratio) cameraConfig.crop.ratio = [] return Picker.openCamera( cameraConfig, (result: CameraResult) => { resolved(result) }, (error: CameraError) => { rejected(error) } ) }) } const DEFAULT_COUNT = 20 const validateLanguage = (language?: Language): Language => { if (!language || !LANGUAGES.includes(language)) { return 'system' } return language } const primaryColor = '#2979ff' export const defaultOptions: Config = { maxSelect: DEFAULT_COUNT, maxVideo: DEFAULT_COUNT, primaryColor, backgroundDark: '#2f2f2f', allowedLimit: true, numberOfColumn: 3, isPreview: true, mediaType: 'all', selectedAssets: [], selectBoxStyle: 'number', selectMode: 'multiple', presentation: 'fullScreenModal', language: 'system', theme: 'system', isHiddenOriginalButton: false, allowSwipeToSelect: true, camera: { cameraDevice: 'back', videoMaximumDuration: 60, }, } const LANGUAGES = [ 'system', 'zh-Hans', 'zh-Hant', 'ja', 'ko', 'en', 'th', 'id', 'vi', 'ru', 'de', 'fr', 'ar', ] as const