Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions DEBUGGING_NAVIGATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Navigation Issue Debugging Guide

## The Problem
- User finishes recording a clip
- Toast appears: "Clip created! Redirecting to your clip..."
- Navigation to `/clip/{id}` doesn't happen
- Browser console shows: "Throttling navigation to prevent the browser from hanging"

## What I've Added

### Console Logging in Capture.tsx
Look for these logs when recording:
1. `handleUploadDone called` - When upload finishes
2. `handleRecordingComplete called` - When full processing completes
3. `saveRecordingToClip called` - When we try to save and navigate
4. `Clip saved to database, navigating to: /clip/xxx` - Just before navigate()
5. `Calling navigate() now` - Right before the navigate call
6. `navigate() called` - Right after the navigate call

### Console Logging in useUser.ts
Look for these logs:
1. `[useUser] Effect running` - When hook mounts/re-runs
2. `[useUser] syncUser called` - When auth sync happens
3. `[useUser] Auth state changed: {event}` - When auth state changes
4. `[useUser] Navigating to login` - If redirecting to login

## How to Debug

### Step 1: Record a clip and watch console
1. Start recording
2. Stop recording (after 3s)
3. Watch the console logs
4. Note the sequence of events

### Step 2: Check for navigation throttling
Look for:
- Is `navigate()` being called multiple times rapidly?
- Is `[useUser]` running repeatedly?
- Are there auth state changes happening?

### Step 3: Identify the blocker
- If you see "Calling navigate() now" but no actual navigation, something is blocking it
- If you see multiple rapid navigate calls, we have a loop
- If you see auth state changes, they might be interfering

## Expected Flow (Normal)
1. User stops recording
2. Video uploads to Livepeer
3. `handleUploadDone` called → `saveRecordingToClip` → `navigate('/clip/xxx')`
4. ClipView page loads
5. Video processes in background
6. Eventually `handleRecordingComplete` called → skipped (clipSavedRef is true)

## Fixes Applied
1. **Race condition guard**: Set `clipSavedRef.current = true` BEFORE calling async saveRecordingToClip
2. **Double-callback prevention**: Check flag in both handleUploadDone and handleRecordingComplete
3. **Error handling**: Reset flag on error so user can retry
4. **Logging**: Added extensive console.logs to trace execution

## Next Steps
Run the app, record a clip, and paste the console logs here to see what's happening.
23 changes: 17 additions & 6 deletions src/hooks/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {

useEffect(() => {
let mounted = true;
let hasNavigated = false;

console.log('[useUser] Effect running, allowSignedOff:', allowSignedOff);

const syncUser = async () => {
console.log('[useUser] syncUser called, mounted:', mounted, 'hasNavigated:', hasNavigated);
try {
// Get current session
const { data: { session: currentSession }, error: sessionError } = await supabase.auth.getSession();

if (sessionError) {
console.error('Error getting session:', sessionError);
if (!allowSignedOff) {
if (!allowSignedOff && !hasNavigated) {
hasNavigated = true;
navigate('/login');
}
return;
Expand All @@ -62,7 +67,9 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {
setUser(null);
setLoading(false);

if (!allowSignedOff) {
if (!allowSignedOff && !hasNavigated) {
console.log('[useUser] Navigating to login (session error)');
hasNavigated = true;
navigate('/login');
}
}
Expand Down Expand Up @@ -161,7 +168,8 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {
console.error('All upsert attempts failed:', lastError);
setLoading(false);

if (!allowSignedOff) {
if (!allowSignedOff && !hasNavigated) {
hasNavigated = true;
navigate('/login');
}
}
Expand All @@ -171,7 +179,8 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {
if (mounted) {
setLoading(false);

if (!allowSignedOff) {
if (!allowSignedOff && !hasNavigated) {
hasNavigated = true;
navigate('/login');
}
}
Expand All @@ -181,7 +190,8 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {
syncUser();

// Listen for auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
console.log('[useUser] Auth state changed:', event, 'mounted:', mounted);
if (!mounted) return;

// Re-run the sync when auth state changes
Expand All @@ -192,7 +202,8 @@ export function useUser(options: UseUserOptions = {}): UseUserReturn {
mounted = false;
subscription.unsubscribe();
};
}, [allowSignedOff, navigate]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allowSignedOff]); // navigate is stable and should not be in deps

return { user, loading, session };
}
40 changes: 30 additions & 10 deletions src/pages/Capture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,7 @@ export default function Capture() {
const saveRecordingToClip = useCallback(
async (result: StudioRecordingResult, complete: boolean) => {
try {
// Skip if we already saved the clip early (via progress callback)
if (clipSavedRef.current) {
console.log("Clip already saved in progress callback, skipping complete handler");
return;
}
console.log("saveRecordingToClip called", { complete, hasRawUrl: !!result.rawUploadedFileUrl });

// Get session ID
const { data: sessionData, error: sessionError } = await supabase
Expand Down Expand Up @@ -413,17 +409,22 @@ export default function Capture() {
tIndexList: canvasParams?.t_index_list || [],
});

// Set flag to avoid double saving on uploadDone/complete
clipSavedRef.current = true;
const clipUrl = `/clip/${clip.id}`;
console.log("Clip saved to database, navigating to:", clipUrl);

toast({
title: "Clip created!",
description: "Redirecting to your clip...",
});

navigate(`/clip/${clip.id}`);
// Navigate immediately - don't wait for anything else
console.log("Calling navigate() now");
navigate(clipUrl);
console.log("navigate() called");
} catch (error: unknown) {
console.error("Error saving clip to database:", error);
// Reset the flag on error so user can try again
clipSavedRef.current = false;
toast({
title: "Error creating clip",
description: error instanceof Error ? error.message : String(error),
Expand All @@ -440,23 +441,42 @@ export default function Capture() {

const handleRecordingComplete = useCallback(
async (result: StudioRecordingResult) => {
console.log("handleRecordingComplete called", { result, clipSaved: clipSavedRef.current });
// Don't call saveRecordingToClip if we already saved via handleUploadDone
if (clipSavedRef.current) {
console.log("Skipping handleRecordingComplete because clip already saved");
return;
}

// Set flag immediately to prevent race condition
clipSavedRef.current = true;
console.log("Recording complete, saving to database...", result);
saveRecordingToClip(result, true);
await saveRecordingToClip(result, true);
},
[saveRecordingToClip]
);

// StudioRecorder callback: Handle upload completion and optimistically create clip
const handleUploadDone = useCallback(
async (result: StudioRecordingResult) => {
console.log("handleUploadDone called", { hasRawUrl: !!result.rawUploadedFileUrl, clipSaved: clipSavedRef.current });

// Only save early if we have rawUploadedFileUrl
if (!result.rawUploadedFileUrl) {
console.log("No rawUploadedFileUrl, will wait for full completion");
return;
}

// Don't save if already saved
if (clipSavedRef.current) {
console.log("Skipping handleUploadDone because clip already saved");
return;
}

// Set flag immediately to prevent race condition
clipSavedRef.current = true;
console.log("Upload complete, saving clip optimistically with raw URL:", result.rawUploadedFileUrl);
saveRecordingToClip(result, false);
await saveRecordingToClip(result, false);
},
[saveRecordingToClip]
);
Expand Down