diff options
author | dan <dan.abramov@gmail.com> | 2024-10-03 11:41:23 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-03 11:41:23 +0900 |
commit | d2392d2d64c46d0fd0b6d97b3b4715b5b8c825d3 (patch) | |
tree | 6c5aaa7e42f6543112140464d084db14b4495987 /src/view/com/composer/Composer.tsx | |
parent | c2dac855cc61e05d21b7dbb81f1fef26f1a9e1e7 (diff) | |
download | voidsky-d2392d2d64c46d0fd0b6d97b3b4715b5b8c825d3.tar.zst |
Refactor video uploads (#5570)
* Remove unused video field * Stop exposing video dispatch * Move cancellation out of the reducer * Make useUploadStatusQuery controlled by jobId * Rename SetStatus to SetProcessing This action only has one callsite and it's always passing "processing". * Move jobId into video reducer state * Make cancellation scoped * Inline useCompressVideoMutation * Move processVideo down the file * Extract getErrorMessage * useServiceAuthToken -> getServiceAuthToken * useVideoAgent -> createVideoAgent * useVideoUploadLimits -> getVideoUploadLimits * useUploadVideoMutation -> uploadVideo * Use async/await in processVideo * Inline onVideoCompressed into processVideo * Use async/await for uploadVideo * Factor out error messages * Guard dispatch with signal This lets us remove the scattered signal checks around dispatch. * Move job polling out of RQ * Handle poll failures * Remove unnecessary guards * Slightly more accurate condition * Move initVideoUri handling out of the hook * Remove dead argument It wasn't being used before either. * Remove unused detailed status This isn't being used because we're only respecting that state variable when isProcessing=true, but isProcessing is always false during video upload. If we want to re-add this later, it should really just be derived from the reducer state. * Harden the video reducer * Tie all spawned work to a signal * Preserve asset/media for nicer error state * Rename actions to match states * Inline useUploadVideo This abstraction is getting in the way of some future work. * Move MIME check to the only place that handles it
Diffstat (limited to 'src/view/com/composer/Composer.tsx')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 124 |
1 files changed, 78 insertions, 46 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index f354f0f0d..185a57fc3 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -36,6 +36,7 @@ import Animated, { ZoomOut, } from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {ImagePickerAsset} from 'expo-image-picker' import { AppBskyFeedDefs, AppBskyFeedGetPostThread, @@ -82,9 +83,10 @@ import {Gif} from '#/state/queries/tenor' import {ThreadgateAllowUISetting} from '#/state/queries/threadgate' import {threadgateViewToAllowUISetting} from '#/state/queries/threadgate/util' import { + createVideoState, + processVideo, State as VideoUploadState, - useUploadVideo, - VideoUploadDispatch, + videoReducer, } from '#/state/queries/video/video' import {useAgent, useSession} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' @@ -147,7 +149,8 @@ export const ComposePost = ({ }) => { const {currentAccount} = useSession() const agent = useAgent() - const {data: currentProfile} = useProfileQuery({did: currentAccount!.did}) + const currentDid = currentAccount!.did + const {data: currentProfile} = useProfileQuery({did: currentDid}) const {isModalActive} = useModals() const {closeComposer} = useComposerControls() const pal = usePalette('default') @@ -189,21 +192,50 @@ export const ComposePost = ({ const [videoAltText, setVideoAltText] = useState('') const [captions, setCaptions] = useState<{lang: string; file: File}[]>([]) - const { - selectVideo, - clearVideo, - state: videoUploadState, - updateVideoDimensions, - dispatch: videoUploadDispatch, - } = useUploadVideo({ - setStatus: setProcessingState, - onSuccess: () => { - if (publishOnUpload) { - onPressPublish(true) - } + const [videoUploadState, videoDispatch] = useReducer( + videoReducer, + undefined, + createVideoState, + ) + + const selectVideo = React.useCallback( + (asset: ImagePickerAsset) => { + processVideo( + asset, + videoDispatch, + agent, + currentDid, + videoUploadState.abortController.signal, + _, + ) }, - initialVideoUri: initVideoUri, - }) + [_, videoUploadState.abortController, videoDispatch, agent, currentDid], + ) + + // Whenever we receive an initial video uri, we should immediately run compression if necessary + useEffect(() => { + if (initVideoUri) { + selectVideo({uri: initVideoUri} as ImagePickerAsset) + } + }, [initVideoUri, selectVideo]) + + const clearVideo = React.useCallback(() => { + videoUploadState.abortController.abort() + videoDispatch({type: 'to_idle', nextController: new AbortController()}) + }, [videoUploadState.abortController, videoDispatch]) + + const updateVideoDimensions = useCallback( + (width: number, height: number) => { + videoDispatch({ + type: 'update_dimensions', + width, + height, + signal: videoUploadState.abortController.signal, + }) + }, + [videoUploadState.abortController], + ) + const hasVideo = Boolean(videoUploadState.asset || videoUploadState.video) const [publishOnUpload, setPublishOnUpload] = useState(false) @@ -400,19 +432,18 @@ export const ComposePost = ({ postgate, onStateChange: setProcessingState, langs: toPostLanguages(langPrefs.postLanguage), - video: videoUploadState.pendingPublish?.blobRef - ? { - blobRef: videoUploadState.pendingPublish.blobRef, - altText: videoAltText, - captions: captions, - aspectRatio: videoUploadState.asset - ? { - width: videoUploadState.asset?.width, - height: videoUploadState.asset?.height, - } - : undefined, - } - : undefined, + video: + videoUploadState.status === 'done' + ? { + blobRef: videoUploadState.pendingPublish.blobRef, + altText: videoAltText, + captions: captions, + aspectRatio: { + width: videoUploadState.asset.width, + height: videoUploadState.asset.height, + }, + } + : undefined, }) ).uri try { @@ -694,7 +725,7 @@ export const ComposePost = ({ error={error} videoUploadState={videoUploadState} clearError={() => setError('')} - videoUploadDispatch={videoUploadDispatch} + clearVideo={clearVideo} /> </Animated.View> <Animated.ScrollView @@ -1083,25 +1114,25 @@ function ErrorBanner({ error: standardError, videoUploadState, clearError, - videoUploadDispatch, + clearVideo, }: { error: string videoUploadState: VideoUploadState clearError: () => void - videoUploadDispatch: VideoUploadDispatch + clearVideo: () => void }) { const t = useTheme() const {_} = useLingui() const videoError = - videoUploadState.status !== 'idle' ? videoUploadState.error : undefined + videoUploadState.status === 'error' ? videoUploadState.error : undefined const error = standardError || videoError const onClearError = () => { if (standardError) { clearError() } else { - videoUploadDispatch({type: 'Reset'}) + clearVideo() } } @@ -1136,7 +1167,7 @@ function ErrorBanner({ <ButtonIcon icon={X} /> </Button> </View> - {videoError && videoUploadState.jobStatus?.jobId && ( + {videoError && videoUploadState.jobId && ( <NewText style={[ {paddingLeft: 28}, @@ -1145,7 +1176,7 @@ function ErrorBanner({ a.leading_snug, t.atoms.text_contrast_low, ]}> - <Trans>Job ID: {videoUploadState.jobStatus.jobId}</Trans> + <Trans>Job ID: {videoUploadState.jobId}</Trans> </NewText> )} </View> @@ -1174,9 +1205,7 @@ function ToolbarWrapper({ function VideoUploadToolbar({state}: {state: VideoUploadState}) { const t = useTheme() const {_} = useLingui() - const progress = state.jobStatus?.progress - ? state.jobStatus.progress / 100 - : state.progress + const progress = state.progress const shouldRotate = state.status === 'processing' && (progress === 0 || progress === 1) let wheelProgress = shouldRotate ? 0.33 : progress @@ -1212,16 +1241,15 @@ function VideoUploadToolbar({state}: {state: VideoUploadState}) { case 'processing': text = _('Processing video...') break + case 'error': + text = _('Error') + wheelProgress = 100 + break case 'done': text = _('Video uploaded') break } - if (state.error) { - text = _('Error') - wheelProgress = 100 - } - return ( <ToolbarWrapper style={[a.flex_row, a.align_center, {paddingVertical: 5}]}> <Animated.View style={[animatedStyle]}> @@ -1229,7 +1257,11 @@ function VideoUploadToolbar({state}: {state: VideoUploadState}) { size={30} borderWidth={1} borderColor={t.atoms.border_contrast_low.borderColor} - color={state.error ? t.palette.negative_500 : t.palette.primary_500} + color={ + state.status === 'error' + ? t.palette.negative_500 + : t.palette.primary_500 + } progress={wheelProgress} /> </Animated.View> |