handle camera cancellation

This commit is contained in:
Samuel Newman
2026-07-28 21:01:35 +03:00
parent 0751420a29
commit b70f8f5ebc
6 changed files with 70 additions and 19 deletions
+45
View File
@@ -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,
})
})
})
+3 -3
View File
@@ -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,
@@ -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
+3
View File
@@ -115,6 +115,9 @@ export function ComposerPrompt() {
const image = await openCamera({
mediaTypes: 'images',
})
if (!image) {
return
}
const imageUris = [
{
+8 -8
View File
@@ -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 () => {
+8 -8
View File
@@ -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 () => {