allow taking videos with camera button

This commit is contained in:
Samuel Newman
2026-02-16 16:53:02 +02:00
parent 79e27e303b
commit cadee015f3
5 changed files with 40 additions and 16 deletions
+15 -3
View File
@@ -3,7 +3,7 @@ import {
getInfoAsync,
readDirectoryAsync,
} from 'expo-file-system/legacy'
import {type ImagePickerResult} from 'expo-image-picker'
import {type ImagePickerAsset, type ImagePickerResult} from 'expo-image-picker'
import ExpoImageCropTool, {
type OpenCropperOptions,
} from '@bsky.app/expo-image-crop-tool'
@@ -57,8 +57,20 @@ export async function openUnifiedPicker(): Promise<ImagePickerResult> {
}
}
export async function openCamera(): Promise<PickerImage> {
return await getFile()
export async function openCamera(): Promise<
PickerImage & {asset: ImagePickerAsset}
> {
const file = await getFile()
return {
...file,
asset: {
uri: file.path,
width: file.width,
height: file.height,
mimeType: file.mime,
type: 'image',
},
}
}
export async function openCropper(opts: OpenCropperOptions) {
+1
View File
@@ -29,6 +29,7 @@ export async function openCamera(customOpts: ImagePickerOptions) {
size: asset.fileSize ?? 0,
width: asset.width,
height: asset.height,
asset,
}
}
+4 -2
View File
@@ -1,11 +1,13 @@
import {type ImagePickerAsset, type ImagePickerOptions} from 'expo-image-picker'
import {type OpenCropperOptions} from '@bsky.app/expo-image-crop-tool'
import {type PickerImage} from './picker.shared'
import {type CameraOpts} from './types'
export {openPicker, openUnifiedPicker} from './picker.shared'
export async function openCamera(_opts: CameraOpts): Promise<PickerImage> {
export async function openCamera(
_opts: ImagePickerOptions,
): Promise<PickerImage & {asset: ImagePickerAsset}> {
throw new Error('openCamera is not supported on web')
}
+2 -1
View File
@@ -1951,7 +1951,8 @@ function ComposerFooter({
/>
<OpenCameraBtn
disabled={media?.type === 'images' ? isMaxImages : !!media}
onAdd={onImageAdd}
onAddImage={onImageAdd}
onAddVideo={asset => onSelectVideo(post.id, asset)}
/>
<SelectGifBtn onSelectGif={onSelectGif} disabled={!!media} />
{!isMobile ? (
+18 -10
View File
@@ -1,9 +1,10 @@
import {useCallback} from 'react'
import {type ImagePickerAsset} from 'expo-image-picker'
import * as MediaLibrary from 'expo-media-library'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {POST_IMG_MAX} from '#/lib/constants'
import {POST_IMG_MAX, VIDEO_MAX_DURATION_MS} from '#/lib/constants'
import {useCameraPermission} from '#/lib/hooks/usePermissions'
import {openCamera} from '#/lib/media/picker'
import {logger} from '#/logger'
@@ -15,14 +16,15 @@ import {IS_NATIVE, IS_WEB_MOBILE} from '#/env'
type Props = {
disabled?: boolean
onAdd: (next: ComposerImage[]) => void
onAddImage: (next: ComposerImage[]) => void
onAddVideo: (asset: ImagePickerAsset) => void
}
export function OpenCameraBtn({disabled, onAdd}: Props) {
export function OpenCameraBtn({disabled, onAddImage, onAddVideo}: Props) {
const {_} = useLingui()
const {requestCameraAccessIfNeeded} = useCameraPermission()
const [mediaPermissionRes, requestMediaPermission] =
MediaLibrary.usePermissions({granularPermissions: ['photo']})
MediaLibrary.usePermissions({granularPermissions: ['photo', 'video']})
const t = useTheme()
const onPressTakePicture = useCallback(async () => {
@@ -34,25 +36,31 @@ export function OpenCameraBtn({disabled, onAdd}: Props) {
await requestMediaPermission()
}
const img = await openCamera({
const result = await openCamera({
mediaTypes: ['images', 'videos'],
aspect: [POST_IMG_MAX.width, POST_IMG_MAX.height],
videoMaxDuration: VIDEO_MAX_DURATION_MS / 1000,
})
// 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
if (mediaPermissionRes) {
await MediaLibrary.createAssetAsync(img.path)
await MediaLibrary.createAssetAsync(result.path)
}
const res = await createComposerImage(img)
onAdd([res])
if (result.mime.startsWith('image')) {
const img = await createComposerImage(result)
onAddImage([img])
} else if (result.mime.startsWith('video')) {
onAddVideo(result.asset)
}
} catch (err: any) {
// ignore
logger.warn('Error using camera', {error: err})
}
}, [
onAdd,
onAddImage,
onAddVideo,
requestCameraAccessIfNeeded,
mediaPermissionRes,
requestMediaPermission,