Remove Video Processing State Overlays and Enable Always-Watchable Videos
System Intent
Remove the "processing..." and "failed" status overlay displays from the memory viewer modal. Videos should be playable and viewable immediately upon upload, regardless of their backend processing state. This improves user experience by making memories accessible without UI blocking, while asynchronous enrichment (graph indexing, metadata extraction) continues in the background.
Mermaid Diagram
graph TB
subgraph MemoryViewer["Memory Viewer Modal"]
VideoList["Video List<br/>from Search"]
VideoModal["Video Modal"]
VideoPlayer["Video Player<br/>(Always Rendered)"]
Metadata["Metadata Display<br/>(Badges, timestamps)"]
end
subgraph Backend["Background Processing"]
Ingest["Ingest Pipeline<br/>(state: pending)"]
Enrichment["Graph Enrichment<br/>(state: processing)"]
Complete["Processing Complete<br/>(state: ready)"]
end
User["User"]
User -->|Select video| VideoModal
VideoModal -->|Load immediately| VideoPlayer
VideoModal -->|Render| Metadata
Metadata -->|Show upload time,<br/>not processing state| Metadata
Backend -.->|Async enrichment<br/>does NOT block playback| Enrichment
Enrichment -.-> Complete
style VideoPlayer fill:#90EE90
style Ingest fill:#FFB6C6
style Enrichment fill:#FFD700 Black-Box Input/Output Contracts
Flow: Load and Display Video in Modal
Inputs: - videoId (string): Unique identifier of the video memory - videoUri (string): Playable media URI (uploaded file path or streaming URL) - processingState (enum: pending, processing, ready, failed): Backend enrichment state - metadata (object): Upload timestamp, duration, dimensions
Outputs: - Modal with rendered video player (always playable) - Metadata display (upload time, duration) - No "processing..." overlay - No "failed" state blocking UI
Processing State Mapping:
| State | Display Behavior | Playable | Notes |
|---|---|---|---|
| pending | Show upload badge | Yes | Video in queue for enrichment |
| processing | Show enrichment badge (optional) | Yes | Async background work ongoing |
| ready | Show ready indicator | Yes | Enrichment complete |
| failed | Show error badge (non-blocking) | Yes | Enrichment failed but playback allowed |
| updated | Y | Field changed in current revision |
Success Output:
{
"videoId": "uuid-1234",
"uri": "s3://bucket/video.mp4",
"duration": 45,
"uploadedAt": "2025-05-10T14:30:00Z",
"state": "processing",
"playbackBlocked": false
}
Failure Output (non-blocking):
{
"videoId": "uuid-1234",
"uri": "s3://bucket/video.mp4",
"error": "Enrichment failed: graph indexing timeout",
"playbackBlocked": false,
"state": "failed"
}
Test Mapping: - apps/mobile/src/screens/__tests__/memory-viewer.test.ts - apps/mobile/src/components/__tests__/video-player.test.ts
Flow: Remove Status Overlay Components
Inputs: - Processing state from backend - Overlay component registry
Outputs: - Conditional rendering removes "processing..." and "failed" overlay divs - Video player remains in viewport with no modal blocking - Optional: Small non-blocking badge/indicator for state
Test Mapping: - apps/mobile/src/components/__tests__/video-status-overlay.test.ts
Flow: Handle Enrichment Completion During Playback
Inputs: - processingState update (pending → processing → ready or failed) - Video currently playing in modal
Outputs: - UI smoothly updates without interrupting playback - Optional badge refresh if shown
Test Mapping: - apps/mobile/src/screens/__tests__/memory-viewer.integration.test.ts
Acceptance Criteria
Test 1: Video Loads and Plays Immediately
Input: - User selects a video with processingState: pending
Pass Criteria: - Video player renders and accepts play command within 1 second - No "processing..." or overlay blocks the video area - Video plays without interruption - Upload timestamp is displayed
Fail Criteria: - Modal shows "processing..." overlay - Play button is disabled or hidden - Video doesn't load
Test 2: Processing State Badge Updates Without Blocking
Input: - User is playing a video with processingState: processing - Backend enrichment completes, state changes to ready
Pass Criteria: - Video continues playing without stutter or interruption - Optional state badge updates (if shown) without re-rendering player - No modal re-opens or overlay appears
Fail Criteria: - Playback pauses or restarts - Overlay appears momentarily - User interaction required to dismiss notification
Test 3: Failed Enrichment Doesn't Block Playback
Input: - User selects a video with processingState: failed - Graph enrichment error occurred in backend
Pass Criteria: - Video player loads and plays normally - Error badge or indicator shown in non-blocking location (corner/badge, not overlay) - User can watch entire video without errors
Fail Criteria: - "Failed" overlay blocks video - Play button is disabled - Modal requires user action to dismiss error
Test 4: All Video States Are Watchable
Input: - Video exists in states: pending, processing, ready, failed
Pass Criteria: - All four states render playable videos - No overlays block playback in any state - Clear visual differentiation (badges/indicators only, not overlays)
Fail Criteria: - Any state blocks playback with overlay - Video URI is not loaded/playable
Test 5: Multiple Concurrent Videos Can Be Opened
Input: - User opens video modal for video A (processing) - User closes modal, opens video B (ready) - User opens video A again
Pass Criteria: - Each video loads and plays independently - No state leakage between modals - Processing of video A doesn't affect video B
Fail Criteria: - State from one video affects another - Modal fails to re-open same video
Pseudocode (Critical Flow: Modal Load and Display)
FUNCTION loadVideoModal(videoId, processingState, videoUri, metadata):
// Always render the modal with player
modal = createModal()
// Load video player without state-based blocking
player = createVideoPlayer()
player.src = videoUri
player.disabled = false // Always playable, regardless of state
modal.appendChild(player)
// Add metadata (upload time, not processing state)
metadataDiv = createMetadataDisplay()
metadataDiv.innerHTML = formatTimestamp(metadata.uploadedAt)
metadataDiv.innerHTML += formatDuration(metadata.duration)
// Conditionally add state badge (non-blocking, optional)
IF processingState != "ready":
badge = createStateBadge(processingState)
badge.style.position = "absolute"
badge.style.top = "10px"
badge.style.right = "10px"
badge.style.zIndex = 5 // Below player controls
modal.appendChild(badge)
END IF
// Remove all blocking overlay components
// DELETE overlay component if processingState == "processing"
// DELETE overlay component if processingState == "failed"
modal.appendChild(metadataDiv)
modal.show()
RETURN modal
FUNCTION createStateBadge(state):
badge = createElement("div")
badge.className = "state-badge state-" + state
badge.innerText = formatStateLabel(state)
RETURN badge
FUNCTION removeStatusOverlay():
overlays = document.querySelectorAll(".video-processing-overlay, .video-failed-overlay")
FOR EACH overlay IN overlays:
overlay.remove()
END FOR
Implementation Notes
- Identify current overlay components: Locate React/JSX components that render processing state overlays (likely in
apps/mobile/src/components/orapps/mobile/src/screens/) - Remove conditional rendering: Strip out
{processingState === 'processing' && <Overlay />}or similar patterns - Ensure video URI is always loaded: Verify
videoUriprop is passed to player component regardless of state - Replace with optional badge: If state visibility is desired for users, use a small non-blocking badge instead
- Test all state transitions: Verify playback continues smoothly when state changes during playback
- Update modal styling: Remove any CSS that hides/disables player based on processing state
Status
Current: draft Next: Await approval of Mermaid diagram, then move to contract and acceptance criteria approval