get select video button + compression working

This commit is contained in:
Samuel Newman
2024-06-05 14:57:38 +03:00
parent 4bd0ff8ba2
commit af02488812
8 changed files with 165 additions and 4 deletions
+16 -3
View File
@@ -91,6 +91,9 @@ import {TextInput, TextInputRef} from './text-input/TextInput'
import {ThreadgateBtn} from './threadgate/ThreadgateBtn'
import {useExternalLinkFetch} from './useExternalLinkFetch'
import hairlineWidth = StyleSheet.hairlineWidth
import {SelectVideoBtn} from './videos/SelectVideoBtn'
import {useVideoState} from './videos/state'
import {VideoPreview} from './videos/VideoPreview'
type CancelRef = {
onPressCancel: () => void
@@ -150,6 +153,9 @@ export const ComposePost = observer(function ComposePost({
const [quote, setQuote] = useState<ComposerOpts['quote'] | undefined>(
initQuote,
)
const {video, onSelectVideo, setVideoPending, videoPending} = useVideoState({
setError,
})
const {extLink, setExtLink} = useExternalLinkFetch({setQuote})
const [extGif, setExtGif] = useState<Gif>()
const [labels, setLabels] = useState<string[]>([])
@@ -358,8 +364,8 @@ export const ComposePost = observer(function ComposePost({
? _(msg`Write your reply`)
: _(msg`What's up?`)
const canSelectImages = gallery.size < 4 && !extLink
const hasMedia = gallery.size > 0 || Boolean(extLink)
const canSelectImages = gallery.size < 4 && !extLink && !video
const hasMedia = gallery.size > 0 || Boolean(extLink) || Boolean(video)
const onEmojiButtonPress = useCallback(() => {
openPicker?.(textInput.current?.getCursorPosition())
@@ -583,7 +589,8 @@ export const ComposePost = observer(function ComposePost({
<QuoteX onRemove={() => setQuote(undefined)} />
)}
</View>
) : undefined}
) : null}
{video && <VideoPreview video={video} />}
</Animated.ScrollView>
<SuggestedLanguage text={richtext.text} />
@@ -602,6 +609,12 @@ export const ComposePost = observer(function ComposePost({
]}>
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
<SelectPhotoBtn gallery={gallery} disabled={!canSelectImages} />
<SelectVideoBtn
onSelectVideo={onSelectVideo}
disabled={!canSelectImages}
pending={videoPending}
setPending={setVideoPending}
/>
<OpenCameraBtn gallery={gallery} disabled={!canSelectImages} />
<SelectGifBtn
onClose={focusTextInput}
@@ -0,0 +1,68 @@
import React, {useCallback} from 'react'
import {
ImagePickerAsset,
launchImageLibraryAsync,
MediaTypeOptions,
} from 'expo-image-picker'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {VideoClip_Stroke2_Corner0_Rounded as VideoClipIcon} from '#/components/icons/VideoClip'
const VIDEO_MAX_DURATION = 90
type Props = {
onSelectVideo: (video: ImagePickerAsset) => void
disabled?: boolean
pending: boolean
setPending: (pending: boolean) => void
}
export function SelectVideoBtn({
onSelectVideo,
disabled,
pending,
setPending,
}: Props) {
const {_} = useLingui()
const t = useTheme()
const onPressSelectVideo = useCallback(async () => {
try {
setPending(true)
const response = await launchImageLibraryAsync({
exif: false,
mediaTypes: MediaTypeOptions.Videos,
videoMaxDuration: VIDEO_MAX_DURATION,
quality: 1,
})
if (response.assets && response.assets.length > 0) {
onSelectVideo(response.assets[0])
}
} finally {
setPending(false)
}
}, [onSelectVideo, setPending])
return (
<>
<Button
testID="openGifBtn"
onPress={onPressSelectVideo}
label={_(msg`Select GIF`)}
accessibilityHint={_(msg`Opens GIF select dialog`)}
style={a.p_sm}
variant="ghost"
shape="round"
color="primary"
disabled={disabled || pending}>
<VideoClipIcon
size="lg"
style={(disabled || pending) && t.atoms.text_contrast_low}
/>
</Button>
</>
)
}
@@ -0,0 +1,8 @@
import React from 'react'
import * as FileSystem from 'expo-file-system'
import {Text} from '#/components/Typography'
export function VideoPreview({video}: {video: FileSystem.FileInfo}) {
return <Text>{JSON.stringify(video, null, 2)}</Text>
}
+50
View File
@@ -0,0 +1,50 @@
import {useState} from 'react'
import * as FileSystem from 'expo-file-system'
import {ImagePickerAsset} from 'expo-image-picker'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useMutation} from '@tanstack/react-query'
import {compressVideo} from '#/lib/media/video/compress'
export function useVideoState({setError}: {setError: (error: string) => void}) {
const [pending, setVideoPending] = useState(false)
const {_} = useLingui()
const {mutate, data, isPending, isError, reset} = useMutation({
mutationFn: async (asset: ImagePickerAsset) => {
const compressed = await compressVideo(asset.uri)
if (!compressed.uri) {
throw new Error('Failed to compress video')
}
const res = await FileSystem.getInfoAsync(compressed.uri, {size: true})
if (res.exists) {
console.log(
'uncompressed size',
(asset.fileSize! / 1024 / 1024).toFixed(2) + 'mb',
)
console.log(
'compressed size',
(res.size / 1024 / 1024).toFixed(2) + 'mb',
)
return res
} else {
throw new Error('Could not find output video')
}
},
onError: error => {
console.error('error', error)
setError(_(msg`Could not compress video`))
},
})
return {
video: data,
onSelectVideo: mutate,
videoPending: pending || isPending,
setVideoPending,
videoError: isError,
clearVideo: reset,
}
}