Compare commits

...

1 Commits

Author SHA1 Message Date
Samuel Newman 99b5a5b275 add modal that lets you pick photo or video 2024-09-13 21:39:57 +01:00
4 changed files with 118 additions and 17 deletions
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M2 5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v2.523l3.629-1.451A1 1 0 0 1 22 7v10a1 1 0 0 1-1.371.928L17 16.477V19a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5Zm2 1v12h11v-3a1 1 0 0 1 1.371-.928L20 15.523V8.477l-3.629 1.451A1 1 0 0 1 15 9V6H4Zm5.5 4.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3ZM6 12a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 449 B

+5
View File
@@ -0,0 +1,5 @@
import {createSinglePathSVG} from './TEMPLATE'
export const FilmCamera_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M2 5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v2.523l3.629-1.451A1 1 0 0 1 22 7v10a1 1 0 0 1-1.371.928L17 16.477V19a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5Zm2 1v12h11v-3a1 1 0 0 1 1.371-.928L20 15.523V8.477l-3.629 1.451A1 1 0 0 1 15 9V6H4Zm5.5 4.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3ZM6 12a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Z',
})
+5 -1
View File
@@ -807,7 +807,11 @@ export const ComposePost = observer(function ComposePost({
disabled={!canSelectImages}
setError={setError}
/>
<OpenCameraBtn gallery={gallery} disabled={!canSelectImages} />
<OpenCameraBtn
gallery={gallery}
disabled={!canSelectImages}
selectVideo={selectVideo}
/>
<SelectGifBtn
onClose={focusTextInput}
onSelectGif={onSelectGif}
+107 -16
View File
@@ -1,6 +1,13 @@
import React, {useCallback} from 'react'
import {Keyboard} from 'react-native'
import {
ImagePickerAsset,
launchCameraAsync,
MediaTypeOptions,
UIImagePickerPresentationStyle,
} from 'expo-image-picker'
import * as MediaLibrary from 'expo-media-library'
import {msg} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useAnalytics} from '#/lib/analytics/analytics'
@@ -11,21 +18,27 @@ import {logger} from '#/logger'
import {isMobileWeb, isNative} from '#/platform/detection'
import {GalleryModel} from '#/state/models/media/gallery'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera'
import {FilmCamera_Stroke2_Corner0_Rounded as FilmCamera} from '#/components/icons/FilmCamera'
const VIDEO_MAX_DURATION = 60
type Props = {
gallery: GalleryModel
disabled?: boolean
selectVideo: (video: ImagePickerAsset) => void
}
export function OpenCameraBtn({gallery, disabled}: Props) {
export function OpenCameraBtn({gallery, disabled, selectVideo}: Props) {
const {track} = useAnalytics()
const {_} = useLingui()
const {requestCameraAccessIfNeeded} = useCameraPermission()
const [mediaPermissionRes, requestMediaPermission] =
MediaLibrary.usePermissions({granularPermissions: ['photo']})
const t = useTheme()
const control = Dialog.useDialogControl()
const onPressTakePicture = useCallback(async () => {
track('Composer:CameraOpened')
@@ -49,7 +62,7 @@ export function OpenCameraBtn({gallery, disabled}: Props) {
await MediaLibrary.createAssetAsync(img.path)
}
gallery.add(img)
} catch (err: any) {
} catch (err) {
// ignore
logger.warn('Error using camera', {error: err})
}
@@ -61,23 +74,101 @@ export function OpenCameraBtn({gallery, disabled}: Props) {
requestMediaPermission,
])
const onPressTakeVideo = useCallback(async () => {
try {
const response = await launchCameraAsync({
mediaTypes: MediaTypeOptions.Videos,
quality: 0.8,
videoMaxDuration: VIDEO_MAX_DURATION,
exif: false,
presentationStyle: UIImagePickerPresentationStyle.PAGE_SHEET,
})
if (!response.canceled && response.assets[0]) {
selectVideo(response.assets[0])
}
} catch (err) {
// ignore
logger.warn('Error using camera', {error: err})
}
}, [selectVideo])
const shouldShowCameraButton = isNative || isMobileWeb
if (!shouldShowCameraButton) {
return null
}
if (isMobileWeb) {
return (
<Button
testID="openCameraButton"
onPress={onPressTakePicture}
label={_(msg`Camera`)}
accessibilityHint={_(msg`Opens camera on device`)}
style={a.p_sm}
variant="ghost"
shape="round"
color="primary"
disabled={disabled}>
<Camera size="lg" style={disabled && t.atoms.text_contrast_low} />
</Button>
)
}
return (
<Button
testID="openCameraButton"
onPress={onPressTakePicture}
label={_(msg`Camera`)}
accessibilityHint={_(msg`Opens camera on device`)}
style={a.p_sm}
variant="ghost"
shape="round"
color="primary"
disabled={disabled}>
<Camera size="lg" style={disabled && t.atoms.text_contrast_low} />
</Button>
<>
<Button
testID="openCameraButton"
onPress={() => {
Keyboard.dismiss()
control.open()
}}
label={_(msg`Camera`)}
accessibilityHint={_(msg`Take a photo or video with your camera`)}
style={a.p_sm}
variant="ghost"
shape="round"
color="primary"
disabled={disabled}>
<Camera size="lg" style={disabled && t.atoms.text_contrast_low} />
</Button>
<Dialog.Outer control={control} testID="selectCameraModeDialog">
<Dialog.Handle />
<Dialog.Inner label={_(msg`Camera Mode`)} style={a.gap_md}>
<Button
label={_(msg`Take a photo`)}
variant="solid"
color="secondary"
size="large"
onPress={() => control.close(onPressTakePicture)}>
<ButtonIcon icon={Camera} />
<ButtonText>
<Trans>Photo</Trans>
</ButtonText>
</Button>
<Button
label={_(msg`Take a video`)}
variant="solid"
color="secondary"
size="large"
onPress={() => control.close(onPressTakeVideo)}>
<ButtonIcon icon={FilmCamera} />
<ButtonText>
<Trans>Video</Trans>
</ButtonText>
</Button>
<Button
label={_(msg`Cancel`)}
variant="ghost"
color="secondary"
size="medium"
onPress={() => control.close()}>
<ButtonText>
<Trans>Cancel</Trans>
</ButtonText>
</Button>
</Dialog.Inner>
</Dialog.Outer>
</>
)
}