diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 5cb96bceb5..f9129c56e9 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -125,6 +125,7 @@ import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as EmojiPicker from '#/components/EmojiPicker'
+import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
@@ -2717,17 +2718,25 @@ function VideoUploadToolbar({state}: {state: VideoState}) {
return (
-
+ {state.status === 'done' ? (
+
+
+
+ ) : (
+
+ )}
{text}
)
diff --git a/src/view/com/composer/state/video.ts b/src/view/com/composer/state/video.ts
index 5becb1568d..eeca7dca47 100644
--- a/src/view/com/composer/state/video.ts
+++ b/src/view/com/composer/state/video.ts
@@ -17,7 +17,11 @@ import {uploadVideo} from '#/lib/media/video/upload'
import {createVideoAgent} from '#/lib/media/video/util'
import {isNetworkError} from '#/lib/strings/errors'
import {logger} from '#/logger'
-import {advanceVideoProgress, videoProgressForPhase} from './videoProgress'
+import {
+ advanceVideoProgress,
+ didSkipVideoCompression,
+ videoProgressForPhase,
+} from './videoProgress'
type CaptionsTrack = {lang: string; file: File}
@@ -331,7 +335,7 @@ export async function processVideo(
dispatch({
type: 'compressing_to_uploading',
video,
- compressionSkipped: video.passthroughReason !== undefined,
+ compressionSkipped: didSkipVideoCompression(video.passthroughReason),
signal,
})
diff --git a/src/view/com/composer/state/videoProgress.test.ts b/src/view/com/composer/state/videoProgress.test.ts
index 53e662c3fe..92d8fc0835 100644
--- a/src/view/com/composer/state/videoProgress.test.ts
+++ b/src/view/com/composer/state/videoProgress.test.ts
@@ -1,9 +1,20 @@
import {
advanceVideoProgress,
+ didSkipVideoCompression,
videoProgressForPhase,
videoProgressWithinPhase,
} from './videoProgress'
+describe('didSkipVideoCompression', () => {
+ it('distinguishes a real skip from a failed compression attempt', () => {
+ expect(didSkipVideoCompression(undefined)).toBe(false)
+ expect(didSkipVideoCompression('below-byte-threshold')).toBe(true)
+ expect(didSkipVideoCompression('no-webcodecs')).toBe(true)
+ expect(didSkipVideoCompression('gif')).toBe(true)
+ expect(didSkipVideoCompression('compress-error-fallback')).toBe(false)
+ })
+})
+
describe('videoProgressForPhase', () => {
it('maps each phase onto one continuous timeline', () => {
expect(videoProgressForPhase('compressing', 0)).toBe(0)
diff --git a/src/view/com/composer/state/videoProgress.ts b/src/view/com/composer/state/videoProgress.ts
index 8d43064c2f..bee9be68fa 100644
--- a/src/view/com/composer/state/videoProgress.ts
+++ b/src/view/com/composer/state/videoProgress.ts
@@ -1,3 +1,5 @@
+import {type VideoCompressSkipReason} from '#/lib/media/video/types'
+
export type VideoProgressPhase =
| 'compressing'
| 'uploading'
@@ -14,6 +16,18 @@ const PHASE_RANGES: Record = {
processing: [0.5, 1],
}
+export function didSkipVideoCompression(
+ passthroughReason: VideoCompressSkipReason | undefined,
+): boolean {
+ // The web compressor can return the original file after reporting partial
+ // compression progress. Keep that case on the post-compression upload band
+ // so the global indicator cannot jump backwards.
+ return (
+ passthroughReason !== undefined &&
+ passthroughReason !== 'compress-error-fallback'
+ )
+}
+
export function videoProgressForPhase(
phase: VideoProgressPhase,
phaseProgress: number,