allow taking videos with camera button
This commit is contained in:
@@ -3,7 +3,7 @@ import {
|
|||||||
getInfoAsync,
|
getInfoAsync,
|
||||||
readDirectoryAsync,
|
readDirectoryAsync,
|
||||||
} from 'expo-file-system/legacy'
|
} from 'expo-file-system/legacy'
|
||||||
import {type ImagePickerResult} from 'expo-image-picker'
|
import {type ImagePickerAsset, type ImagePickerResult} from 'expo-image-picker'
|
||||||
import ExpoImageCropTool, {
|
import ExpoImageCropTool, {
|
||||||
type OpenCropperOptions,
|
type OpenCropperOptions,
|
||||||
} from '@bsky.app/expo-image-crop-tool'
|
} from '@bsky.app/expo-image-crop-tool'
|
||||||
@@ -57,8 +57,20 @@ export async function openUnifiedPicker(): Promise<ImagePickerResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openCamera(): Promise<PickerImage> {
|
export async function openCamera(): Promise<
|
||||||
return await getFile()
|
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) {
|
export async function openCropper(opts: OpenCropperOptions) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export async function openCamera(customOpts: ImagePickerOptions) {
|
|||||||
size: asset.fileSize ?? 0,
|
size: asset.fileSize ?? 0,
|
||||||
width: asset.width,
|
width: asset.width,
|
||||||
height: asset.height,
|
height: asset.height,
|
||||||
|
asset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 OpenCropperOptions} from '@bsky.app/expo-image-crop-tool'
|
||||||
|
|
||||||
import {type PickerImage} from './picker.shared'
|
import {type PickerImage} from './picker.shared'
|
||||||
import {type CameraOpts} from './types'
|
|
||||||
|
|
||||||
export {openPicker, openUnifiedPicker} from './picker.shared'
|
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')
|
throw new Error('openCamera is not supported on web')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1951,7 +1951,8 @@ function ComposerFooter({
|
|||||||
/>
|
/>
|
||||||
<OpenCameraBtn
|
<OpenCameraBtn
|
||||||
disabled={media?.type === 'images' ? isMaxImages : !!media}
|
disabled={media?.type === 'images' ? isMaxImages : !!media}
|
||||||
onAdd={onImageAdd}
|
onAddImage={onImageAdd}
|
||||||
|
onAddVideo={asset => onSelectVideo(post.id, asset)}
|
||||||
/>
|
/>
|
||||||
<SelectGifBtn onSelectGif={onSelectGif} disabled={!!media} />
|
<SelectGifBtn onSelectGif={onSelectGif} disabled={!!media} />
|
||||||
{!isMobile ? (
|
{!isMobile ? (
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
|
import {type ImagePickerAsset} from 'expo-image-picker'
|
||||||
import * as MediaLibrary from 'expo-media-library'
|
import * as MediaLibrary from 'expo-media-library'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
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 {useCameraPermission} from '#/lib/hooks/usePermissions'
|
||||||
import {openCamera} from '#/lib/media/picker'
|
import {openCamera} from '#/lib/media/picker'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -15,14 +16,15 @@ import {IS_NATIVE, IS_WEB_MOBILE} from '#/env'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
disabled?: boolean
|
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 {_} = useLingui()
|
||||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||||
const [mediaPermissionRes, requestMediaPermission] =
|
const [mediaPermissionRes, requestMediaPermission] =
|
||||||
MediaLibrary.usePermissions({granularPermissions: ['photo']})
|
MediaLibrary.usePermissions({granularPermissions: ['photo', 'video']})
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
const onPressTakePicture = useCallback(async () => {
|
const onPressTakePicture = useCallback(async () => {
|
||||||
@@ -34,25 +36,31 @@ export function OpenCameraBtn({disabled, onAdd}: Props) {
|
|||||||
await requestMediaPermission()
|
await requestMediaPermission()
|
||||||
}
|
}
|
||||||
|
|
||||||
const img = await openCamera({
|
const result = await openCamera({
|
||||||
|
mediaTypes: ['images', 'videos'],
|
||||||
aspect: [POST_IMG_MAX.width, POST_IMG_MAX.height],
|
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
|
// 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
|
// the image even without these permissions
|
||||||
if (mediaPermissionRes) {
|
if (mediaPermissionRes) {
|
||||||
await MediaLibrary.createAssetAsync(img.path)
|
await MediaLibrary.createAssetAsync(result.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await createComposerImage(img)
|
if (result.mime.startsWith('image')) {
|
||||||
|
const img = await createComposerImage(result)
|
||||||
onAdd([res])
|
onAddImage([img])
|
||||||
|
} else if (result.mime.startsWith('video')) {
|
||||||
|
onAddVideo(result.asset)
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// ignore
|
// ignore
|
||||||
logger.warn('Error using camera', {error: err})
|
logger.warn('Error using camera', {error: err})
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
onAdd,
|
onAddImage,
|
||||||
|
onAddVideo,
|
||||||
requestCameraAccessIfNeeded,
|
requestCameraAccessIfNeeded,
|
||||||
mediaPermissionRes,
|
mediaPermissionRes,
|
||||||
requestMediaPermission,
|
requestMediaPermission,
|
||||||
|
|||||||
Reference in New Issue
Block a user