Replace image picker with expo-image-picker (#649)

* Replace image picker with expo-image-picker

* Fix cropper & picker on web

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
zio/stable
Ollie H 2023-05-16 15:38:32 -07:00 committed by GitHub
parent d5bec4ff37
commit 5f66adc9a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 46 deletions

View File

@ -75,7 +75,7 @@
"expo-device": "~5.2.1", "expo-device": "~5.2.1",
"expo-image": "^1.2.1", "expo-image": "^1.2.1",
"expo-image-manipulator": "^11.1.1", "expo-image-manipulator": "^11.1.1",
"expo-image-picker": "~14.1.1", "expo-image-picker": "^14.1.1",
"expo-localization": "~14.1.1", "expo-localization": "~14.1.1",
"expo-media-library": "~15.2.3", "expo-media-library": "~15.2.3",
"expo-sharing": "~11.2.2", "expo-sharing": "~11.2.2",

View File

@ -1,12 +1,16 @@
import { import {
openPicker as openPickerFn,
openCamera as openCameraFn, openCamera as openCameraFn,
openCropper as openCropperFn, openCropper as openCropperFn,
ImageOrVideo, Image as RNImage,
} from 'react-native-image-crop-picker' } from 'react-native-image-crop-picker'
import {RootStoreModel} from 'state/index' import {RootStoreModel} from 'state/index'
import {PickerOpts, CameraOpts, CropperOptions} from './types' import {CameraOpts, CropperOptions} from './types'
import {Image as RNImage} from 'react-native-image-crop-picker' import {
ImagePickerOptions,
launchImageLibraryAsync,
MediaTypeOptions,
} from 'expo-image-picker'
import {getDataUriSize} from './util'
/** /**
* NOTE * NOTE
@ -19,27 +23,22 @@ import {Image as RNImage} from 'react-native-image-crop-picker'
export async function openPicker( export async function openPicker(
_store: RootStoreModel, _store: RootStoreModel,
opts?: PickerOpts, opts?: ImagePickerOptions,
): Promise<RNImage[]> { ) {
const items = await openPickerFn({ const response = await launchImageLibraryAsync({
mediaType: 'photo', // TODO: eventually add other media types exif: false,
multiple: opts?.multiple, mediaTypes: MediaTypeOptions.Images,
maxFiles: opts?.maxFiles, quality: 1,
forceJpg: true, // ios only ...opts,
compressImageQuality: 0.8,
}) })
const toMedia = (item: ImageOrVideo) => ({ return (response.assets ?? []).map(image => ({
path: item.path, mime: 'image/jpeg',
mime: item.mime, height: image.height,
size: item.size, width: image.width,
width: item.width, path: image.uri,
height: item.height, size: getDataUriSize(image.uri),
}) }))
if (Array.isArray(items)) {
return items.map(toMedia)
}
return [toMedia(items)]
} }
export async function openCamera( export async function openCamera(
@ -55,6 +54,7 @@ export async function openCamera(
forceJpg: true, // ios only forceJpg: true, // ios only
compressImageQuality: 0.8, compressImageQuality: 0.8,
}) })
return { return {
path: item.path, path: item.path,
mime: item.mime, mime: item.mime,
@ -67,11 +67,10 @@ export async function openCamera(
export async function openCropper( export async function openCropper(
_store: RootStoreModel, _store: RootStoreModel,
opts: CropperOptions, opts: CropperOptions,
): Promise<RNImage> { ) {
const item = await openCropperFn({ const item = await openCropperFn({
...opts, ...opts,
forceJpg: true, // ios only forceJpg: true, // ios only
compressImageQuality: 0.8,
}) })
return { return {

View File

@ -102,10 +102,14 @@ export class GalleryModel {
async pick() { async pick() {
const images = await openPicker(this.rootStore, { const images = await openPicker(this.rootStore, {
multiple: true, selectionLimit: 4 - this.size,
maxFiles: 4 - this.images.length, allowsMultipleSelection: true,
}) })
await Promise.all(images.map(image => this.add(image))) return await Promise.all(
images.map(image => {
this.add(image)
}),
)
} }
} }

View File

@ -135,7 +135,7 @@ export class ImageModel implements RNImage {
// Only for mobile // Only for mobile
async crop() { async crop() {
try { try {
const cropped = await openCropper(this.rootStore, { const cropped = await openCropper({
mediaType: 'photo', mediaType: 'photo',
path: this.path, path: this.path,
freeStyleCropEnabled: true, freeStyleCropEnabled: true,

View File

@ -65,7 +65,7 @@ export function Component({
} }
const onSelectNewAvatar = useCallback( const onSelectNewAvatar = useCallback(
async (img: RNImage | null) => { async (img: RNImage | null) => {
if (!img) { if (img === null) {
setNewUserAvatar(null) setNewUserAvatar(null)
setUserAvatar(null) setUserAvatar(null)
return return
@ -81,6 +81,7 @@ export function Component({
}, },
[track, setNewUserAvatar, setUserAvatar, setError], [track, setNewUserAvatar, setUserAvatar, setError],
) )
const onSelectNewBanner = useCallback( const onSelectNewBanner = useCallback(
async (img: RNImage | null) => { async (img: RNImage | null) => {
if (!img) { if (!img) {
@ -99,6 +100,7 @@ export function Component({
}, },
[track, setNewUserBanner, setUserBanner, setError], [track, setNewUserBanner, setUserBanner, setError],
) )
const onPressSave = useCallback(async () => { const onPressSave = useCallback(async () => {
track('EditProfile:Save') track('EditProfile:Save')
setProcessing(true) setProcessing(true)

View File

@ -66,6 +66,7 @@ export function UserAvatar({
if (!(await requestCameraAccessIfNeeded())) { if (!(await requestCameraAccessIfNeeded())) {
return return
} }
onSelectNewAvatar?.( onSelectNewAvatar?.(
await openCamera(store, { await openCamera(store, {
width: 1000, width: 1000,
@ -83,20 +84,21 @@ export function UserAvatar({
if (!(await requestPhotoAccessIfNeeded())) { if (!(await requestPhotoAccessIfNeeded())) {
return return
} }
const items = await openPicker(store, { const items = await openPicker(store, {
aspect: [1, 1],
})
const item = items[0]
const croppedImage = await openCropper(store, {
mediaType: 'photo', mediaType: 'photo',
multiple: false, cropperCircleOverlay: true,
height: item.height,
width: item.width,
path: item.path,
}) })
onSelectNewAvatar?.( onSelectNewAvatar?.(croppedImage)
await openCropper(store, {
mediaType: 'photo',
path: items[0].path,
width: 1000,
height: 1000,
cropperCircleOverlay: true,
}),
)
}, },
}, },
{ {

View File

@ -55,10 +55,8 @@ export function UserBanner({
if (!(await requestPhotoAccessIfNeeded())) { if (!(await requestPhotoAccessIfNeeded())) {
return return
} }
const items = await openPicker(store, { const items = await openPicker(store)
mediaType: 'photo',
multiple: false,
})
onSelectNewBanner?.( onSelectNewBanner?.(
await openCropper(store, { await openCropper(store, {
mediaType: 'photo', mediaType: 'photo',

View File

@ -8731,7 +8731,7 @@ expo-image-manipulator@^11.1.1:
dependencies: dependencies:
expo-image-loader "~4.1.0" expo-image-loader "~4.1.0"
expo-image-picker@~14.1.1: expo-image-picker@^14.1.1:
version "14.1.1" version "14.1.1"
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-14.1.1.tgz#181f1348ba6a43df7b87cee4a601d45c79b7c2d7" resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-14.1.1.tgz#181f1348ba6a43df7b87cee4a601d45c79b7c2d7"
integrity sha512-SvWtnkLW7jp5Ntvk3lVcRQmhFYja8psmiR7O6P/+7S6f4llt3vaFwb4I3+pUXqJxxpi7BHc2+95qOLf0SFOIag== integrity sha512-SvWtnkLW7jp5Ntvk3lVcRQmhFYja8psmiR7O6P/+7S6f4llt3vaFwb4I3+pUXqJxxpi7BHc2+95qOLf0SFOIag==