[Video] Error handling in composer, fix auto-send (#5122)
* tweak * error state for upload toolbar * catch errors in upload status query * stop query on error --------- Co-authored-by: Hailey <me@haileyok.com>zio/stable
parent
0bd0146efb
commit
3ee5ef32d9
|
@ -1,4 +1,4 @@
|
||||||
import React, {useCallback} from 'react'
|
import React, {useCallback, useEffect} from 'react'
|
||||||
import {ImagePickerAsset} from 'expo-image-picker'
|
import {ImagePickerAsset} from 'expo-image-picker'
|
||||||
import {AppBskyVideoDefs, BlobRef} from '@atproto/api'
|
import {AppBskyVideoDefs, BlobRef} from '@atproto/api'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
@ -25,7 +25,7 @@ type Action =
|
||||||
| {type: 'SetDimensions'; width: number; height: number}
|
| {type: 'SetDimensions'; width: number; height: number}
|
||||||
| {type: 'SetVideo'; video: CompressedVideo}
|
| {type: 'SetVideo'; video: CompressedVideo}
|
||||||
| {type: 'SetJobStatus'; jobStatus: AppBskyVideoDefs.JobStatus}
|
| {type: 'SetJobStatus'; jobStatus: AppBskyVideoDefs.JobStatus}
|
||||||
| {type: 'SetBlobRef'; blobRef: BlobRef}
|
| {type: 'SetComplete'; blobRef: BlobRef}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
status: Status
|
status: Status
|
||||||
|
@ -36,6 +36,7 @@ export interface State {
|
||||||
blobRef?: BlobRef
|
blobRef?: BlobRef
|
||||||
error?: string
|
error?: string
|
||||||
abortController: AbortController
|
abortController: AbortController
|
||||||
|
pendingPublish?: {blobRef: BlobRef; mutableProcessed: boolean}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reducer(queryClient: QueryClient) {
|
function reducer(queryClient: QueryClient) {
|
||||||
|
@ -77,8 +78,15 @@ function reducer(queryClient: QueryClient) {
|
||||||
updatedState = {...state, video: action.video, status: 'uploading'}
|
updatedState = {...state, video: action.video, status: 'uploading'}
|
||||||
} else if (action.type === 'SetJobStatus') {
|
} else if (action.type === 'SetJobStatus') {
|
||||||
updatedState = {...state, jobStatus: action.jobStatus}
|
updatedState = {...state, jobStatus: action.jobStatus}
|
||||||
} else if (action.type === 'SetBlobRef') {
|
} else if (action.type === 'SetComplete') {
|
||||||
updatedState = {...state, blobRef: action.blobRef, status: 'done'}
|
updatedState = {
|
||||||
|
...state,
|
||||||
|
pendingPublish: {
|
||||||
|
blobRef: action.blobRef,
|
||||||
|
mutableProcessed: false,
|
||||||
|
},
|
||||||
|
status: 'done',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
|
@ -86,7 +94,6 @@ function reducer(queryClient: QueryClient) {
|
||||||
|
|
||||||
export function useUploadVideo({
|
export function useUploadVideo({
|
||||||
setStatus,
|
setStatus,
|
||||||
onSuccess,
|
|
||||||
}: {
|
}: {
|
||||||
setStatus: (status: string) => void
|
setStatus: (status: string) => void
|
||||||
onSuccess: () => void
|
onSuccess: () => void
|
||||||
|
@ -112,11 +119,16 @@ export function useUploadVideo({
|
||||||
},
|
},
|
||||||
onSuccess: blobRef => {
|
onSuccess: blobRef => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SetBlobRef',
|
type: 'SetComplete',
|
||||||
blobRef,
|
blobRef,
|
||||||
})
|
})
|
||||||
onSuccess()
|
|
||||||
},
|
},
|
||||||
|
onError: useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'SetError',
|
||||||
|
error: _(msg`Video failed to process`),
|
||||||
|
})
|
||||||
|
}, [_]),
|
||||||
})
|
})
|
||||||
|
|
||||||
const {mutate: onVideoCompressed} = useUploadVideoMutation({
|
const {mutate: onVideoCompressed} = useUploadVideoMutation({
|
||||||
|
@ -215,15 +227,17 @@ export function useUploadVideo({
|
||||||
const useUploadStatusQuery = ({
|
const useUploadStatusQuery = ({
|
||||||
onStatusChange,
|
onStatusChange,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
onError,
|
||||||
}: {
|
}: {
|
||||||
onStatusChange: (status: AppBskyVideoDefs.JobStatus) => void
|
onStatusChange: (status: AppBskyVideoDefs.JobStatus) => void
|
||||||
onSuccess: (blobRef: BlobRef) => void
|
onSuccess: (blobRef: BlobRef) => void
|
||||||
|
onError: (error: Error) => void
|
||||||
}) => {
|
}) => {
|
||||||
const videoAgent = useVideoAgent()
|
const videoAgent = useVideoAgent()
|
||||||
const [enabled, setEnabled] = React.useState(true)
|
const [enabled, setEnabled] = React.useState(true)
|
||||||
const [jobId, setJobId] = React.useState<string>()
|
const [jobId, setJobId] = React.useState<string>()
|
||||||
|
|
||||||
const {isLoading, isError} = useQuery({
|
const {error} = useQuery({
|
||||||
queryKey: ['video', 'upload status', jobId],
|
queryKey: ['video', 'upload status', jobId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!jobId) return // this won't happen, can ignore
|
if (!jobId) return // this won't happen, can ignore
|
||||||
|
@ -245,9 +259,14 @@ const useUploadStatusQuery = ({
|
||||||
refetchInterval: 1500,
|
refetchInterval: 1500,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) {
|
||||||
|
onError(error)
|
||||||
|
setEnabled(false)
|
||||||
|
}
|
||||||
|
}, [error, onError])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading,
|
|
||||||
isError,
|
|
||||||
setJobId: (_jobId: string) => {
|
setJobId: (_jobId: string) => {
|
||||||
setJobId(_jobId)
|
setJobId(_jobId)
|
||||||
setEnabled(true)
|
setEnabled(true)
|
||||||
|
|
|
@ -190,6 +190,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const [publishOnUpload, setPublishOnUpload] = useState(false)
|
const [publishOnUpload, setPublishOnUpload] = useState(false)
|
||||||
|
|
||||||
const {extLink, setExtLink} = useExternalLinkFetch({setQuote, setError})
|
const {extLink, setExtLink} = useExternalLinkFetch({setQuote, setError})
|
||||||
|
@ -303,7 +304,8 @@ export const ComposePost = observer(function ComposePost({
|
||||||
return false
|
return false
|
||||||
}, [gallery.needsAltText, extLink, extGif, requireAltTextEnabled])
|
}, [gallery.needsAltText, extLink, extGif, requireAltTextEnabled])
|
||||||
|
|
||||||
const onPressPublish = async (finishedUploading?: boolean) => {
|
const onPressPublish = React.useCallback(
|
||||||
|
async (finishedUploading?: boolean) => {
|
||||||
if (isProcessing || graphemeLength > MAX_GRAPHEME_LENGTH) {
|
if (isProcessing || graphemeLength > MAX_GRAPHEME_LENGTH) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -353,9 +355,9 @@ export const ComposePost = observer(function ComposePost({
|
||||||
postgate,
|
postgate,
|
||||||
onStateChange: setProcessingState,
|
onStateChange: setProcessingState,
|
||||||
langs: toPostLanguages(langPrefs.postLanguage),
|
langs: toPostLanguages(langPrefs.postLanguage),
|
||||||
video: videoUploadState.blobRef
|
video: videoUploadState.pendingPublish?.blobRef
|
||||||
? {
|
? {
|
||||||
blobRef: videoUploadState.blobRef,
|
blobRef: videoUploadState.pendingPublish.blobRef,
|
||||||
altText: videoAltText,
|
altText: videoAltText,
|
||||||
captions: captions,
|
captions: captions,
|
||||||
aspectRatio: videoUploadState.asset
|
aspectRatio: videoUploadState.asset
|
||||||
|
@ -443,7 +445,46 @@ export const ComposePost = observer(function ComposePost({
|
||||||
? _(msg`Your reply has been published`)
|
? _(msg`Your reply has been published`)
|
||||||
: _(msg`Your post has been published`),
|
: _(msg`Your post has been published`),
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
agent,
|
||||||
|
captions,
|
||||||
|
extLink,
|
||||||
|
gallery.images,
|
||||||
|
gallery.isEmpty,
|
||||||
|
gallery.size,
|
||||||
|
graphemeLength,
|
||||||
|
isAltTextRequiredAndMissing,
|
||||||
|
isProcessing,
|
||||||
|
labels,
|
||||||
|
langPrefs.postLanguage,
|
||||||
|
onClose,
|
||||||
|
onPost,
|
||||||
|
postgate,
|
||||||
|
quote,
|
||||||
|
quoteCount,
|
||||||
|
replyTo,
|
||||||
|
richtext.text,
|
||||||
|
setExtLink,
|
||||||
|
setLangPrefs,
|
||||||
|
threadgateAllowUISettings,
|
||||||
|
track,
|
||||||
|
videoAltText,
|
||||||
|
videoUploadState.asset,
|
||||||
|
videoUploadState.pendingPublish,
|
||||||
|
videoUploadState.status,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (videoUploadState.pendingPublish && publishOnUpload) {
|
||||||
|
if (!videoUploadState.pendingPublish.mutableProcessed) {
|
||||||
|
videoUploadState.pendingPublish.mutableProcessed = true
|
||||||
|
onPressPublish(true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}, [onPressPublish, publishOnUpload, videoUploadState.pendingPublish])
|
||||||
|
|
||||||
const canPost = useMemo(
|
const canPost = useMemo(
|
||||||
() => graphemeLength <= MAX_GRAPHEME_LENGTH && !isAltTextRequiredAndMissing,
|
() => graphemeLength <= MAX_GRAPHEME_LENGTH && !isAltTextRequiredAndMissing,
|
||||||
|
@ -1058,18 +1099,23 @@ function VideoUploadToolbar({state}: {state: VideoUploadState}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we could use state.jobStatus?.progress but 99% of the time it jumps from 0 to 100
|
// we could use state.jobStatus?.progress but 99% of the time it jumps from 0 to 100
|
||||||
const progress =
|
let progress =
|
||||||
state.status === 'compressing' || state.status === 'uploading'
|
state.status === 'compressing' || state.status === 'uploading'
|
||||||
? state.progress
|
? state.progress
|
||||||
: 100
|
: 100
|
||||||
|
|
||||||
|
if (state.error) {
|
||||||
|
text = _('Error')
|
||||||
|
progress = 100
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolbarWrapper style={[a.flex_row, a.align_center, {paddingVertical: 5}]}>
|
<ToolbarWrapper style={[a.flex_row, a.align_center, {paddingVertical: 5}]}>
|
||||||
<ProgressCircle
|
<ProgressCircle
|
||||||
size={30}
|
size={30}
|
||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
borderColor={t.atoms.border_contrast_low.borderColor}
|
borderColor={t.atoms.border_contrast_low.borderColor}
|
||||||
color={t.palette.primary_500}
|
color={state.error ? t.palette.negative_500 : t.palette.primary_500}
|
||||||
progress={progress}
|
progress={progress}
|
||||||
/>
|
/>
|
||||||
<NewText style={[a.font_bold, a.ml_sm]}>{text}</NewText>
|
<NewText style={[a.font_bold, a.ml_sm]}>{text}</NewText>
|
||||||
|
|
Loading…
Reference in New Issue