import analytics from '@components/analytics' import { ActionSheetOptions } from '@expo/react-native-action-sheet' import { store } from '@root/store' import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice' import * as ExpoImagePicker from 'expo-image-picker' import i18next from 'i18next' import { Alert, Linking } from 'react-native' import ImagePicker, { Image, ImageOrVideo } from 'react-native-image-crop-picker' export interface Props { mediaType?: 'photo' | 'video' resize?: { width?: number; height?: number } maximum?: number indicateMaximum?: boolean showActionSheetWithOptions: ( options: ActionSheetOptions, callback: (i?: number | undefined) => void | Promise ) => void } const mediaSelector = async ({ mediaType, resize, maximum, indicateMaximum = false, showActionSheetWithOptions }: Props): Promise => { const checkLibraryPermission = async (): Promise => { const { status } = await ExpoImagePicker.requestMediaLibraryPermissionsAsync() if (status !== 'granted') { Alert.alert( i18next.t('componentMediaSelector:library.alert.title'), i18next.t('componentMediaSelector:library.alert.message'), [ { text: i18next.t('common:buttons.cancel'), style: 'cancel', onPress: () => analytics('mediaSelector_nopermission', { action: 'cancel' }) }, { text: i18next.t( 'componentMediaSelector:library.alert.buttons.settings' ), style: 'default', onPress: () => { analytics('mediaSelector_nopermission', { action: 'settings' }) Linking.openURL('app-settings:') } } ] ) return false } else { return true } } const _maximum = maximum || getInstanceConfigurationStatusMaxAttachments(store.getState()) || 4 const options = () => { switch (mediaType) { case 'photo': return [ i18next.t( 'componentMediaSelector:options.image', indicateMaximum ? { context: 'max', max: _maximum } : undefined ), i18next.t('common:buttons.cancel') ] case 'video': return [ i18next.t( 'componentMediaSelector:options.video', indicateMaximum ? { context: 'max', max: 1 } : undefined ), i18next.t('common:buttons.cancel') ] default: return [ i18next.t( 'componentMediaSelector:options.image', indicateMaximum ? { context: 'max', max: _maximum } : undefined ), i18next.t( 'componentMediaSelector:options.video', indicateMaximum ? { context: 'max', max: 1 } : undefined ), i18next.t('common:buttons.cancel') ] } } return new Promise((resolve, reject) => { const selectImage = async () => { const images = await ImagePicker.openPicker({ mediaType: 'photo', includeExif: false, multiple: true, minFiles: 1, maxFiles: _maximum, loadingLabelText: '', compressImageMaxWidth: 4096, compressImageMaxHeight: 4096 }).catch(() => {}) if (!images) { return reject() } if (!resize) { return resolve(images) } else { const croppedImages: Image[] = [] for (const image of images) { const croppedImage = await ImagePicker.openCropper({ mediaType: 'photo', path: image.path, width: resize.width, height: resize.height, cropperChooseText: i18next.t('common:buttons.apply'), cropperCancelText: i18next.t('common:buttons.cancel'), hideBottomControls: true }).catch(() => {}) croppedImage && croppedImages.push(croppedImage) } return resolve(croppedImages) } } const selectVideo = async () => { const video = await ImagePicker.openPicker({ mediaType: 'video', includeExif: false, loadingLabelText: '' }).catch(() => {}) if (video) { return resolve([video]) } else { return reject() } } showActionSheetWithOptions( { title: i18next.t('componentMediaSelector:title'), options: options(), cancelButtonIndex: mediaType ? 1 : 2 }, async buttonIndex => { if (!(await checkLibraryPermission())) { return reject() } switch (mediaType) { case 'photo': if (buttonIndex === 0) { await selectImage() } break case 'video': if (buttonIndex === 0) { await selectVideo() } break default: if (buttonIndex === 0) { await selectImage() } else if (buttonIndex === 1) { await selectVideo() } break } } ) }) } export default mediaSelector