diff --git a/src/lib/media/__tests__/picker.test.ts b/src/lib/media/__tests__/picker.test.ts new file mode 100644 index 0000000000..5f287e1f32 --- /dev/null +++ b/src/lib/media/__tests__/picker.test.ts @@ -0,0 +1,45 @@ +import {launchCameraAsync} from 'expo-image-picker' + +import {openCamera} from '../picker' + +jest.mock('expo-image-picker', () => ({ + launchCameraAsync: jest.fn(), +})) +jest.mock('@bsky.app/expo-image-crop-tool', () => ({})) +jest.mock('../picker.shared', () => ({})) + +const mockLaunchCameraAsync = jest.mocked(launchCameraAsync) + +describe('openCamera', () => { + it('returns undefined when the user cancels', async () => { + mockLaunchCameraAsync.mockResolvedValue({ + assets: null, + canceled: true, + }) + + await expect(openCamera({})).resolves.toBeUndefined() + }) + + it('maps the selected image', async () => { + mockLaunchCameraAsync.mockResolvedValue({ + assets: [ + { + uri: 'file:///photo.jpg', + width: 1200, + height: 800, + mimeType: 'image/jpeg', + fileSize: 1234, + }, + ], + canceled: false, + }) + + await expect(openCamera({})).resolves.toEqual({ + path: 'file:///photo.jpg', + width: 1200, + height: 800, + mime: 'image/jpeg', + size: 1234, + }) + }) +}) diff --git a/src/lib/media/picker.tsx b/src/lib/media/picker.tsx index c9a52b8bad..926b2e9560 100644 --- a/src/lib/media/picker.tsx +++ b/src/lib/media/picker.tsx @@ -17,11 +17,11 @@ export async function openCamera(customOpts: ImagePickerOptions) { } const res = await launchCameraAsync(opts) - if (!res || !res.assets) { - throw new Error('Camera was closed before taking a photo') + if (res.canceled) { + return } - const asset = res?.assets[0] + const asset = res.assets[0] return { path: asset.uri, diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 6365765fc9..cf087b59bb 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -32,6 +32,9 @@ export function OpenCameraBtn({disabled, onAdd}: OpenCameraBtnProps) { const img = await openCamera({ aspect: [1, 1], }) + if (!img) { + return + } // If we don't have permissions it's fine, we just wont save it. The post itself will still have access to // the image even without these permissions diff --git a/src/view/com/feeds/ComposerPrompt.tsx b/src/view/com/feeds/ComposerPrompt.tsx index b221b0fae3..a2effdf231 100644 --- a/src/view/com/feeds/ComposerPrompt.tsx +++ b/src/view/com/feeds/ComposerPrompt.tsx @@ -115,6 +115,9 @@ export function ComposerPrompt() { const image = await openCamera({ mediaTypes: 'images', }) + if (!image) { + return + } const imageUris = [ { diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index f139542857..98819bf8d6 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -391,14 +391,14 @@ let EditableUserAvatar = ({ return } - onSelectNewAvatar( - await compressIfNeeded( - await openCamera({ - aspect: [1, 1], - }), - IMAGE_SIZE_CONFIG_2K_1MB, - ), - ) + const image = await openCamera({ + aspect: [1, 1], + }) + if (!image) { + return + } + + onSelectNewAvatar(await compressIfNeeded(image, IMAGE_SIZE_CONFIG_2K_1MB)) }, [onSelectNewAvatar, requestCameraAccessIfNeeded]) const onOpenLibrary = useCallback(async () => { diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index ff4b6631bd..964cc2ba57 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -58,14 +58,14 @@ export function UserBanner({ if (!(await requestCameraAccessIfNeeded())) { return } - onSelectNewBanner?.( - await compressIfNeeded( - await openCamera({ - aspect: [3, 1], - }), - IMAGE_SIZE_CONFIG_2K_1MB, - ), - ) + const image = await openCamera({ + aspect: [3, 1], + }) + if (!image) { + return + } + + onSelectNewBanner?.(await compressIfNeeded(image, IMAGE_SIZE_CONFIG_2K_1MB)) }, [onSelectNewBanner, requestCameraAccessIfNeeded]) const onOpenLibrary = useCallback(async () => {