Skip to content

feat: add video upload hook#8697

Draft
jaco-brink wants to merge 3 commits intofeature/26-01-JB-story-1from
jacobusbrink/nes-1288-create-video-upload-dropzone-mux
Draft

feat: add video upload hook#8697
jaco-brink wants to merge 3 commits intofeature/26-01-JB-story-1from
jacobusbrink/nes-1288-create-video-upload-dropzone-mux

Conversation

@jaco-brink
Copy link
Contributor

@jaco-brink jaco-brink commented Feb 5, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added video upload functionality with Mux integration, including file validation, progress tracking, and status management.
    • Supports cancellation and error handling with user callbacks for completion and failure scenarios.
  • Tests

    • Comprehensive test suite added covering upload flows, error handling, file validation, and polling behavior.

@linear
Copy link

linear bot commented Feb 5, 2026

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review

Walkthrough

A new React hook useVideoUpload is introduced for handling video uploads via Mux, complete with file validation, progress tracking, polling-based readiness checks, and comprehensive test coverage. The hook and its types are exposed through public exports in the module's index file.

Changes

Cohort / File(s) Summary
Video Upload Hook Implementation
apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.tsx
Introduces new useVideoUpload hook for Mux-based video uploads with GraphQL integration, file size validation (1GB limit), UpChunk-powered file transfer, progress reporting, backend polling for readiness detection, error handling, and cancellation support. Exports GraphQL mutation, query, and supporting types.
Video Upload Tests
apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.spec.tsx
Comprehensive test suite covering successful uploads with progress tracking, upload error handling, large file rejection, upload cancellation, polling state transitions, and cleanup on unmount. Uses mocked UpChunk, Dropzone, and GraphQL responses.
Public API Exports
apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/index.ts
Adds public exports for useVideoUpload hook and VideoUploadStatus type to expose the module's public API.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Client (React Hook)
    participant GraphQL as GraphQL Backend
    participant UpChunk as UpChunk
    participant Mux as Mux Service

    User->>Client: Drop video file
    activate Client
    Client->>Client: Validate file size
    Note over Client: Check < 1GB limit
    
    Client->>GraphQL: createMuxVideoUploadByFile mutation
    activate GraphQL
    GraphQL->>Mux: Request upload session
    Mux-->>GraphQL: Return uploadUrl & id
    GraphQL-->>Client: Return uploadUrl & videoId
    deactivate GraphQL
    
    Client->>UpChunk: createUpload with file & uploadUrl
    activate UpChunk
    UpChunk->>Mux: Upload file chunks
    UpChunk-->>Client: Progress updates
    Client->>Client: Track progress (0-100%)
    Mux-->>UpChunk: Upload complete
    UpChunk-->>Client: Success notification
    deactivate UpChunk
    
    Client->>GraphQL: Poll GET_MY_MUX_VIDEO_QUERY (every 5s)
    activate GraphQL
    loop Until readyToStream
        GraphQL->>Mux: Check video status
        Mux-->>GraphQL: Status: processing/ready
        GraphQL-->>Client: Return readyToStream flag
        Note over Client: Repeat until ready
    end
    GraphQL-->>Client: readyToStream = true
    deactivate GraphQL
    
    Client->>Client: Set status to 'completed'
    Client->>Client: Invoke onUploadComplete callback
    deactivate Client
    User->>User: Video upload finished
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested reviewers

  • edmonday
  • jianwei1
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add video upload hook' accurately describes the main change—introducing a new React hook for video uploads integrated with Mux.
Linked Issues check ✅ Passed The PR implements the core requirement from NES-1288: creates a video upload utility integrated with Mux, and correctly excludes subtitle generation as specified.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the video upload hook and its exports as specified in NES-1288; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jacobusbrink/nes-1288-create-video-upload-dropzone-mux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jaco-brink jaco-brink self-assigned this Feb 5, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

Warnings
⚠️ ❗ Big PR (663 changes)

(change count - 663): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against 4d51633

@nx-cloud
Copy link

nx-cloud bot commented Feb 5, 2026

View your CI Pipeline Execution ↗ for commit 4d51633

Command Status Duration Result
nx run-many --target=vercel-alias --projects=jo... ✅ Succeeded 2s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 12s View ↗
nx run-many --target=deploy --projects=journeys... ✅ Succeeded 2m 42s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-05 03:08:29 UTC

@github-actions github-actions bot temporarily deployed to Preview - journeys-admin February 5, 2026 01:34 Inactive
@jaco-brink
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys-admin ✅ Ready journeys-admin preview Thu Feb 5 16:07:54 NZDT 2026

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.tsx`:
- Around line 68-75: The cancelUpload callback currently aborts the upload and
resets status/progress/error but doesn't clear the uploaded video's identifier,
leaving a stale videoId in the UI; update cancelUpload (the function using
uploadInstanceRef, clearPolling, setStatus, setProgress, setError) to also reset
the video id state by calling the relevant setter (e.g., setVideoId(undefined)
or setVideoId('')) so the UI no longer displays the previous upload's id after
cancel.
- Around line 111-115: When an oversized file is selected the handler currently
only calls setError and returns, leaving previous status/progress/videoId intact
and not notifying the consumer; update the oversized-file branch inside the
async (file: File) => { ... } handler in useVideoUpload to also
setStatus('error'), reset progress (setProgress(0)), clear videoId
(setVideoId(undefined) or null as used), and invoke onUploadError with an
appropriate Error object so the error flows through the callback before
returning. Ensure you use the existing state setters (setError, setStatus,
setProgress, setVideoId) and the onUploadError prop to make the failure state
consistent.
🧹 Nitpick comments (3)
apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.tsx (2)

51-51: Prefer ReturnType<typeof setInterval> for timer refs in browser code.
This avoids Node vs DOM timer type mismatches in TS builds.

♻️ Suggested update
-  const pollingIntervalRef = useRef<NodeJS.Timeout | null>(null)
+  const pollingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)

53-59: Type the Apollo hooks with generated GraphQL types.
Import TemplateCustomizeCreateMuxVideoUploadByFileMutation, TemplateCustomizeCreateMuxVideoUploadByFileMutationVariables, TemplateCustomizeGetMyMuxVideoQuery, and TemplateCustomizeGetMyMuxVideoQueryVariables from the generated types and apply them: useMutation<TemplateCustomizeCreateMuxVideoUploadByFileMutation, TemplateCustomizeCreateMuxVideoUploadByFileMutationVariables>(...) and useLazyQuery<TemplateCustomizeGetMyMuxVideoQuery, TemplateCustomizeGetMyMuxVideoQueryVariables>(...). This avoids any leakage and keeps mutation/query results aligned with the schema.

apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.spec.tsx (1)

246-291: Consider fake timers to avoid 10–20s real-time waits.
Using jest.useFakeTimers() + advanceTimersByTime will speed up CI and reduce flakiness.

Also applies to: 293-345

Comment on lines +68 to +75
const cancelUpload = useCallback(() => {
uploadInstanceRef.current?.abort()
uploadInstanceRef.current = null
clearPolling()
setStatus('idle')
setProgress(0)
setError(undefined)
}, [clearPolling])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reset videoId on cancel to avoid stale state.
Without clearing, the UI can show the previous upload’s id after a cancel.

🧹 Suggested fix
   const cancelUpload = useCallback(() => {
     uploadInstanceRef.current?.abort()
     uploadInstanceRef.current = null
     clearPolling()
     setStatus('idle')
     setProgress(0)
     setError(undefined)
+    setVideoId(undefined)
   }, [clearPolling])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cancelUpload = useCallback(() => {
uploadInstanceRef.current?.abort()
uploadInstanceRef.current = null
clearPolling()
setStatus('idle')
setProgress(0)
setError(undefined)
}, [clearPolling])
const cancelUpload = useCallback(() => {
uploadInstanceRef.current?.abort()
uploadInstanceRef.current = null
clearPolling()
setStatus('idle')
setProgress(0)
setError(undefined)
setVideoId(undefined)
}, [clearPolling])
🤖 Prompt for AI Agents
In
`@apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.tsx`
around lines 68 - 75, The cancelUpload callback currently aborts the upload and
resets status/progress/error but doesn't clear the uploaded video's identifier,
leaving a stale videoId in the UI; update cancelUpload (the function using
uploadInstanceRef, clearPolling, setStatus, setProgress, setError) to also reset
the video id state by calling the relevant setter (e.g., setVideoId(undefined)
or setVideoId('')) so the UI no longer displays the previous upload's id after
cancel.

Comment on lines +111 to +115
async (file: File) => {
if (file.size > MAX_VIDEO_SIZE) {
setError('File is too large. Max size is 1GB.')
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Make oversized-file handling set error status and trigger onUploadError.
Currently status/progress/videoId can stay from a prior upload and errors don’t flow through the callback.

🛡️ Suggested fix
     async (file: File) => {
       if (file.size > MAX_VIDEO_SIZE) {
-        setError('File is too large. Max size is 1GB.')
+        const message = 'File is too large. Max size is 1GB.'
+        setStatus('error')
+        setProgress(0)
+        setVideoId(undefined)
+        setError(message)
+        onUploadError?.(message)
         return
       }
🤖 Prompt for AI Agents
In
`@apps/journeys-admin/src/components/TemplateCustomization/utils/useVideoUpload/useVideoUpload.tsx`
around lines 111 - 115, When an oversized file is selected the handler currently
only calls setError and returns, leaving previous status/progress/videoId intact
and not notifying the consumer; update the oversized-file branch inside the
async (file: File) => { ... } handler in useVideoUpload to also
setStatus('error'), reset progress (setProgress(0)), clear videoId
(setVideoId(undefined) or null as used), and invoke onUploadError with an
appropriate Error object so the error flows through the callback before
returning. Ensure you use the existing state setters (setError, setStatus,
setProgress, setVideoId) and the onUploadError prop to make the failure state
consistent.

@github-actions github-actions bot temporarily deployed to Preview - journeys-admin February 5, 2026 03:03 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant