This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

fix omnifeed auth

+183 -393
+171 -329
src/components/CollectionsFeed/CollectionsFeed.js
··· 8 8 import MatterLoadingAnimation from '../MatterLoadingAnimation'; 9 9 import { Helmet } from 'react-helmet'; 10 10 import { useAuth } from '../../contexts/AuthContext'; 11 + import Loading from '../Loading/Loading'; 11 12 12 13 const CollectionsFeed = () => { 13 14 const { username } = useParams(); 14 15 const navigate = useNavigate(); 15 - const { isAuthenticated, checkAuthStatus } = useAuth(); 16 + const { isAuthenticated, loading: authLoading, session } = useAuth(); 16 17 17 18 // Initialize state variables 18 19 const [handle, setHandle] = useState(username || ''); ··· 154 155 } 155 156 156 157 try { 157 - // Verify authentication before making the request 158 - const authResult = await checkAuthStatus(); 159 - 160 - if (!authResult) { 161 - setError('You are not authenticated. Please log in again.'); 162 - const returnUrl = encodeURIComponent(window.location.pathname); 163 - navigate(`/login?returnUrl=${returnUrl}`); 164 - return; 165 - } 166 - 167 158 const response = await fetch(url, { 168 159 credentials: 'include' 169 160 }); ··· 171 162 if (!response.ok) { 172 163 if (response.status === 401) { 173 164 // Try to refresh auth once 174 - const refreshResult = await checkAuthStatus(); 165 + const refreshResult = await isAuthenticated(); 175 166 if (!refreshResult) { 176 167 setError('Your session has expired. Please log in again.'); 177 168 const returnUrl = encodeURIComponent(window.location.pathname); ··· 355 346 setFetchingMore(false); 356 347 setChartLoading(false); 357 348 } 358 - }, [navigate, checkAuthStatus]); 349 + }, [useRkeyTimestamp, isAuthenticated, navigate, collectionCursors]); 359 350 360 351 // Now define loadUserData after fetchCollectionRecords is defined 361 - const loadUserData = useCallback(async (usernameOrDid) => { 362 - if (!usernameOrDid || !isAuthenticated) { 363 - setError('Please enter a username or DID, and ensure you are logged in.'); 364 - return; 365 - } 366 - 367 - // Reset state for new search 352 + const loadUserData = useCallback(async (userToLoad) => { 353 + console.log(`loadUserData called for: ${userToLoad}`); 368 354 setLoading(true); 369 - setShowContent(false); // Hide content while loading 370 355 setError(''); 371 - setDid(''); 372 - setServiceEndpoint(''); 356 + setSearchPerformed(true); 373 357 setCollections([]); 374 - setSelectedCollections([]); 375 358 setRecords([]); 376 359 setAllRecordsForChart([]); 360 + setSelectedCollections([]); 377 361 setCollectionCursors({}); 378 - 362 + setDid(''); 363 + setHandle(''); 364 + setDisplayName(''); 365 + setServiceEndpoint(''); 366 + setDisplayCount(25); // Reset display count on new search 367 + 379 368 try { 380 - // First, verify authentication is still valid 381 - const authResult = await checkAuthStatus(); 382 - 383 - if (!authResult) { 384 - setError('Your session has expired. Please log in again.'); 385 - setLoading(false); 386 - setTimeout(() => { 387 - const returnUrl = encodeURIComponent(window.location.pathname); 388 - navigate(`/login?returnUrl=${returnUrl}`); 389 - }, 3000); 390 - return; 369 + const identifier = userToLoad || session?.handle; 370 + if (!identifier) { 371 + throw new Error("No user identifier available to load data."); 391 372 } 392 - 393 - // Continue with resolving the handle to DID 394 - let userDid = usernameOrDid; 395 - 396 - // If input doesn't look like a DID, try to resolve it as a handle 397 - if (!userDid.startsWith('did:')) { 398 - try { 399 - userDid = await resolveHandleToDid(usernameOrDid); 400 - } catch (resolveErr) { 401 - setError(`Could not resolve handle: ${resolveErr.message}`); 402 - setLoading(false); 403 - return; 404 - } 373 + 374 + // Resolve identifier first 375 + const resolveResponse = await fetch(`/api/resolve-identifier?identifier=${encodeURIComponent(identifier)}`); 376 + if (!resolveResponse.ok) { 377 + throw new Error(`Failed to resolve identifier: ${identifier}`); 405 378 } 406 - 407 - // Get service endpoint 408 - let endpoint; 409 - try { 410 - endpoint = await getServiceEndpointForDid(userDid); 411 - setServiceEndpoint(endpoint); 412 - } catch (endpointError) { 413 - console.error('Error getting service endpoint:', endpointError); 414 - setError(`Could not determine PDS endpoint for "${userDid}". The user's server may be offline.`); 415 - setInitialLoad(false); 416 - setLoading(false); 417 - return; 418 - } 419 - 420 - // Fetch profile information 421 - try { 422 - const publicApiEndpoint = "https://public.api.bsky.app"; 423 - const profileResponse = await fetch(`${publicApiEndpoint}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(userDid)}`); 424 - 425 - if (!profileResponse.ok) { 426 - throw new Error(`Error fetching profile: ${profileResponse.statusText}`); 427 - } 428 - 429 - const profileData = await profileResponse.json(); 430 - setHandle(profileData.handle); 431 - setDisplayName(profileData.displayName || profileData.handle); 432 - } catch (profileError) { 433 - console.error('Error fetching profile:', profileError); 434 - // Continue without profile data, not critical 435 - } 436 - 437 - // Use our server-side API to fetch collections 438 - let retryCount = 0; 439 - const maxRetries = 2; 440 - let collectionsData; 441 - 442 - while (retryCount <= maxRetries) { 443 - try { 444 - // Verify authentication before making the request 445 - const authResult = await checkAuthStatus(); 446 - 447 - if (!authResult) { 448 - setError('Your session has expired. Please log in again.'); 449 - setLoading(false); 450 - setTimeout(() => { 451 - const returnUrl = encodeURIComponent(window.location.pathname); 452 - navigate(`/login?returnUrl=${returnUrl}`); 453 - }, 3000); 454 - return; 455 - } 456 - 457 - const collectionsResponse = await fetch(`/api/collections/${encodeURIComponent(userDid)}?endpoint=${encodeURIComponent(endpoint)}`, { 458 - credentials: 'include' 459 - }); 460 - 461 - if (!collectionsResponse.ok) { 462 - if (collectionsResponse.status === 401) { 463 - // Try to refresh auth once more 464 - const refreshResult = await checkAuthStatus(); 465 - if (!refreshResult) { 466 - setError('You are not authenticated. Please log in to continue.'); 467 - setLoading(false); 468 - setTimeout(() => { 469 - const returnUrl = encodeURIComponent(window.location.pathname); 470 - navigate(`/login?returnUrl=${returnUrl}`); 471 - }, 3000); 472 - return; 473 - } 474 - // If we're still here, try again 475 - retryCount++; 476 - continue; 477 - } 478 - 479 - // Try to parse the error response 480 - let errorMessage; 481 - try { 482 - const errorData = await collectionsResponse.json(); 483 - errorMessage = errorData?.error || errorData?.details || collectionsResponse.statusText; 484 - } catch (jsonErr) { 485 - errorMessage = collectionsResponse.statusText || `HTTP ${collectionsResponse.status}`; 486 - } 487 - 488 - throw new Error(`Error fetching collections: ${errorMessage}`); 489 - } 490 - 491 - collectionsData = await collectionsResponse.json(); 492 - break; // Success, exit the retry loop 493 - } catch (err) { 494 - console.error(`Attempt ${retryCount + 1} failed:`, err); 495 - if (retryCount === maxRetries) { 496 - // This was our last attempt, propagate the error 497 - throw err; 498 - } 499 - // Wait before retrying 500 - await new Promise(resolve => setTimeout(resolve, 1000 * (retryCount + 1))); 501 - retryCount++; 502 - } 379 + const resolveData = await resolveResponse.json(); 380 + const resolvedDid = resolveData.did; 381 + const resolvedHandle = resolveData.handle; 382 + setDid(resolvedDid); 383 + setHandle(resolvedHandle); 384 + console.log('Resolved:', resolvedDid, resolvedHandle); 385 + 386 + // Fetch collections using resolved DID 387 + // Assume default endpoint or fetch dynamically if needed 388 + const defaultEndpoint = 'https://bsky.social'; // Or determine dynamically 389 + setServiceEndpoint(defaultEndpoint); 390 + 391 + // Use server-side endpoint for collections 392 + const collectionsResponse = await fetch(`/api/collections/${encodeURIComponent(resolvedDid)}?endpoint=${encodeURIComponent(defaultEndpoint)}`, { 393 + credentials: 'include' 394 + }); 395 + 396 + if (!collectionsResponse.ok) { 397 + if (collectionsResponse.status === 401) { 398 + // No need to call checkAuthStatus, just redirect 399 + throw new Error('Authentication required. Please log in again.'); 400 + } 401 + throw new Error(`Failed to fetch collections for ${resolvedDid}`); 503 402 } 504 - 505 - if (collectionsData.collections && collectionsData.collections.length > 0) { 506 - const sortedCollections = [...collectionsData.collections].sort(); 507 - setCollections(sortedCollections); 508 - // By default, select all collections 509 - setSelectedCollections(sortedCollections); 510 - 511 - // Fetch records for each collection 512 - try { 513 - await fetchCollectionRecords(userDid, endpoint, sortedCollections); 514 - } catch (recordsError) { 515 - console.error('Error fetching collection records:', recordsError); 516 - setError(`Successfully loaded collections, but could not load records: ${recordsError.message}`); 517 - // Continue with the collections we have 518 - } 519 - } else { 520 - setError('No collections found for this user.'); 403 + 404 + const collectionsData = await collectionsResponse.json(); 405 + const fetchedCollections = collectionsData.collections || []; 406 + setDisplayName(collectionsData.displayName || resolvedHandle); 407 + setCollections(fetchedCollections); 408 + setSelectedCollections(fetchedCollections); // Select all by default 409 + console.log('Fetched collections:', fetchedCollections); 410 + 411 + // Fetch records for all collections initially for the chart 412 + if (fetchedCollections.length > 0) { 413 + await fetchCollectionRecords(resolvedDid, defaultEndpoint, fetchedCollections); 521 414 } 522 - 523 - setSearchPerformed(true); 524 - setInitialLoad(false); 525 - setLoading(false); 526 - // Add a slight delay before showing content for smooth transition 527 - setTimeout(() => setShowContent(true), 100); 415 + 528 416 } catch (err) { 529 417 console.error('Error loading user data:', err); 530 - 531 - // Provide more user-friendly error messages 532 - let userMessage = 'An error occurred while loading data.'; 533 - 534 - if (err.message) { 535 - if (err.message.includes('authenticated')) { 536 - userMessage = 'Authentication error: Please log in again.'; 537 - setTimeout(() => { 538 - const returnUrl = encodeURIComponent(window.location.pathname); 539 - navigate(`/login?returnUrl=${returnUrl}`); 540 - }, 3000); 541 - } else if (err.message.includes('fetch')) { 542 - userMessage = 'Network error: Could not connect to the server.'; 543 - } else if (err.message.includes('collections')) { 544 - userMessage = `Collections error: ${err.message}`; 545 - } else { 546 - userMessage = err.message; 547 - } 418 + let userMessage = 'Failed to load user data. Please check the handle and try again.'; 419 + if (err.message?.includes('Authentication required')) { 420 + userMessage = 'Authentication error. Please log in again.'; 421 + // Redirect to login 422 + const returnUrl = encodeURIComponent(window.location.pathname); 423 + navigate(`/login?returnUrl=${returnUrl}`); 424 + return; // Stop further execution 548 425 } 549 - 550 426 setError(userMessage); 551 - setInitialLoad(false); 427 + } finally { 552 428 setLoading(false); 553 - setShowContent(true); // Show content even on error so user can see error message 429 + setInitialLoad(false); 430 + setShowContent(true); 554 431 } 555 - }, [navigate, checkAuthStatus]); 432 + }, [session?.handle, navigate]); 556 433 557 434 // Now place useEffects after all the callbacks are defined 558 435 559 - // Verify authentication first 436 + // Effect to handle initial authentication and data loading 560 437 useEffect(() => { 561 - const verifyAuth = async () => { 562 - try { 563 - setLoading(true); 564 - setInitialLoad(true); 565 - setShowContent(false); 566 - 567 - // Reset states if username changes 568 - if (username) { 569 - setError(''); 570 - setSearchTerm(username); 571 - } 572 - 573 - const authResult = await checkAuthStatus(); 574 - 575 - if (!authResult) { 576 - console.log('Not authenticated, redirecting to login'); 577 - // Save the current path for redirect after login 578 - const returnUrl = encodeURIComponent(window.location.pathname); 579 - navigate(`/login?returnUrl=${returnUrl}`); 580 - return; 581 - } 582 - 583 - // If a username is provided in the URL, load that user's data 584 - if (username && authResult) { 585 - console.log('Username provided in URL, loading data for:', username); 586 - loadUserData(username); 587 - } else { 588 - // Only set loading to false if we're not loading a specific user 589 - setLoading(false); 590 - setInitialLoad(false); 591 - setShowContent(true); 592 - } 593 - } catch (err) { 594 - console.error('Auth verification failed:', err); 595 - setError('Authentication failed. Please try logging in again.'); 438 + // Wait for AuthProvider to finish loading 439 + if (authLoading) { 440 + console.log("CollectionsFeed: Waiting for AuthProvider..."); 441 + return; 442 + } 443 + 444 + console.log("CollectionsFeed: AuthProvider loaded. isAuthenticated:", isAuthenticated); 445 + setInitialLoad(true); // Start initial load process 446 + setShowContent(false); 447 + 448 + if (!isAuthenticated) { 449 + console.log('CollectionsFeed: Not authenticated, redirecting to login'); 450 + const returnUrl = encodeURIComponent(window.location.pathname + window.location.search); 451 + navigate(`/login?returnUrl=${returnUrl}`); 452 + return; // Stop execution 453 + } 454 + 455 + // If authenticated, proceed to load data 456 + setError(''); // Clear previous errors 457 + if (username) { 458 + console.log('CollectionsFeed: Authenticated, username provided, loading data for:', username); 459 + setSearchTerm(username); // Update search term from URL 460 + loadUserData(username); 461 + } else if (session?.handle) { 462 + // If no username in URL, load data for the logged-in user 463 + console.log('CollectionsFeed: Authenticated, no username in URL, loading data for logged-in user:', session.handle); 464 + setSearchTerm(session.handle); 465 + loadUserData(session.handle); 466 + } else { 467 + // Authenticated but no username in URL and no handle in session (should not happen ideally) 468 + console.error('CollectionsFeed: Authenticated but no identifier found to load data.'); 469 + setError('Could not determine user to load data for.'); 596 470 setLoading(false); 597 471 setInitialLoad(false); 598 472 setShowContent(true); 599 - 600 - // Add a delay before redirecting to show the error message 601 - setTimeout(() => { 602 - navigate('/login'); 603 - }, 2000); 604 - } 605 - }; 606 - 607 - verifyAuth(); 608 - 609 - // Set up periodic auth checks 610 - const interval = setInterval(async () => { 611 - const authResult = await checkAuthStatus(); 612 - if (!authResult && isAuthenticated) { 613 - // Session was lost during browsing 614 - setError('Your session has expired. Please log in again.'); 615 - setInitialLoad(false); 616 - // Show the error for 3 seconds before redirecting 617 - setTimeout(() => { 618 - const returnUrl = encodeURIComponent(window.location.pathname); 619 - navigate(`/login?returnUrl=${returnUrl}`); 620 - }, 3000); 621 - } 622 - }, 30000); // Check every 30 seconds 623 - 624 - // Safety timeout to ensure initialLoad is cleared after 10 seconds maximum 473 + } 474 + 475 + // Safety timeout for initial load UI state 625 476 const loadingTimeout = setTimeout(() => { 626 - setInitialLoad(false); 627 - }, 10000); 628 - 477 + if (initialLoad) { 478 + console.warn("CollectionsFeed: Initial load timeout reached."); 479 + setInitialLoad(false); 480 + // Decide whether to show content or error based on current state 481 + if (!error) setShowContent(true); 482 + } 483 + }, 15000); // Increase timeout slightly 484 + 485 + // Cleanup function (no interval to clear anymore) 629 486 return () => { 630 - clearInterval(interval); 631 487 clearTimeout(loadingTimeout); 632 488 }; 633 - }, [isAuthenticated, checkAuthStatus, navigate, username, loadUserData]); 489 + // Depend on auth state and username param 490 + }, [isAuthenticated, authLoading, navigate, username, session?.handle, loadUserData]); 634 491 635 492 // Effect to watch for selected collections changes 636 493 useEffect(() => { ··· 709 566 710 567 // Handle refresh button click - only updates the feed, not the chart 711 568 const handleRefresh = async () => { 569 + // Check auth status directly 570 + if (!isAuthenticated) { 571 + console.warn("Refresh cancelled: User not authenticated."); 572 + navigate('/login'); // Redirect to login if session lost 573 + return; 574 + } 712 575 if (did && serviceEndpoint) { 713 - // Set a loading state but not for the chart 714 576 setLoading(true); 715 - 716 - // Reset display count to show only the first 25 records after refresh 717 577 setDisplayCount(25); 718 - 719 578 try { 720 - // Fetch just the most recent records for the feed display 721 - const refreshOnlyFeed = async () => { 722 579 let recentRecords = []; 723 - 724 - // Process each selected collection sequentially 725 580 for (const collection of selectedCollections) { 726 - try { 727 - // Use server-side API endpoint for fetching records 728 - const url = `/api/collections/${encodeURIComponent(did)}/records?endpoint=${encodeURIComponent(serviceEndpoint)}&collection=${encodeURIComponent(collection)}&limit=25`; 729 - 730 - const response = await fetch(url, { 731 - credentials: 'include' 732 - }); 733 - 734 - if (!response.ok) { 735 - if (response.status === 401) { 736 - // Handle unauthorized 737 - checkAuthStatus(); 738 - throw new Error('Authentication required. Please log in again.'); 739 - } 740 - console.error(`Error refreshing ${collection}: ${response.statusText}`); 741 - continue; // Skip this collection but continue with others 581 + try { 582 + const url = `/api/collections/${encodeURIComponent(did)}/records?endpoint=${encodeURIComponent(serviceEndpoint)}&collection=${encodeURIComponent(collection)}&limit=25`; 583 + const response = await fetch(url, { credentials: 'include' }); 584 + if (!response.ok) { 585 + if (response.status === 401) { 586 + // Directly navigate to login on 401 587 + throw new Error('Authentication required. Please log in again.'); 588 + } 589 + console.error(`Error refreshing ${collection}: ${response.statusText}`); 590 + continue; 591 + } 592 + const data = await response.json(); 593 + if (data.records && data.records.length > 0) { 594 + const processedRecords = data.records.map(record => { 595 + const contentTimestamp = extractTimestamp(record); 596 + const rkey = record.uri.split('/').pop(); 597 + const rkeyTimestamp = tidToTimestamp(rkey); 598 + return { 599 + ...record, 600 + collection, 601 + collectionType: record.value?.$type || collection, 602 + contentTimestamp, 603 + rkeyTimestamp, 604 + rkey, 605 + }; 606 + }); 607 + recentRecords = [...recentRecords, ...processedRecords]; 608 + } 609 + } catch (err) { 610 + console.error(`Error refreshing collection ${collection}:`, err); 611 + if (err.message?.includes('Authentication required')) throw err; // Re-throw auth error 612 + // Continue with other collections 742 613 } 743 - 744 - const data = await response.json(); 745 - 746 - if (data.records && data.records.length > 0) { 747 - // Process the records with timestamps 748 - const processedRecords = data.records.map(record => { 749 - const contentTimestamp = extractTimestamp(record); 750 - const rkey = record.uri.split('/').pop(); 751 - const rkeyTimestamp = tidToTimestamp(rkey); 752 - 753 - return { 754 - ...record, 755 - collection, 756 - collectionType: record.value?.$type || collection, 757 - contentTimestamp, 758 - rkeyTimestamp, 759 - rkey, 760 - }; 761 - }); 762 - 763 - // Add to our records array 764 - recentRecords = [...recentRecords, ...processedRecords]; 765 - } 766 - } catch (err) { 767 - console.error(`Error refreshing collection ${collection}:`, err); 768 - // Continue with other collections 769 - } 770 614 } 771 - 772 - // Sort the refreshed records by timestamp (newest first) 773 615 const sortedRecords = recentRecords.filter(record => { 774 - if (useRkeyTimestamp) { 775 - return record.rkeyTimestamp !== null; 776 - } else { 777 - return record.contentTimestamp !== null; 778 - } 616 + if (useRkeyTimestamp) { 617 + return record.rkeyTimestamp !== null; 618 + } else { 619 + return record.contentTimestamp !== null; 620 + } 779 621 }).sort((a, b) => { 780 - const aTime = useRkeyTimestamp ? a.rkeyTimestamp : a.contentTimestamp; 781 - const bTime = useRkeyTimestamp ? b.rkeyTimestamp : b.contentTimestamp; 782 - return new Date(bTime) - new Date(aTime); 622 + const aTime = useRkeyTimestamp ? a.rkeyTimestamp : a.contentTimestamp; 623 + const bTime = useRkeyTimestamp ? b.rkeyTimestamp : b.contentTimestamp; 624 + return new Date(bTime) - new Date(aTime); 783 625 }); 784 - 785 - // Only update the feed display records, not the chart data 786 626 setRecords(sortedRecords.slice(0, 25)); 787 - }; 788 - 789 - await refreshOnlyFeed(); 790 627 } catch (err) { 791 - console.error("Error during feed refresh:", err); 792 - setError('Failed to refresh records. Please try again.'); 628 + console.error("Error during feed refresh:", err); 629 + if (err.message?.includes('Authentication required')) { 630 + const returnUrl = encodeURIComponent(window.location.pathname); 631 + navigate(`/login?returnUrl=${returnUrl}`); 632 + } else { 633 + setError('Failed to refresh records. Please try again.'); 634 + } 793 635 } finally { 794 - // Ensure loading state is reset 795 - setLoading(false); 636 + setLoading(false); 796 637 } 797 638 } 798 639 }; ··· 801 642 const handleLoadMore = async () => { 802 643 // Verify authentication before proceeding 803 644 if (!isAuthenticated) { 804 - checkAuthStatus(); 645 + console.warn("Load more cancelled: User not authenticated."); 646 + navigate('/login'); // Redirect to login if session lost 805 647 return; 806 648 } 807 649
+12 -64
src/components/ProtectedRoute.js
··· 1 - import React, { useEffect, useRef, useState } from 'react'; 1 + import React, { useEffect, useState } from 'react'; 2 2 import { Navigate, useLocation } from 'react-router-dom'; 3 3 import { useAuth } from '../contexts/AuthContext'; 4 4 import { isAccountAllowed } from '../config/allowlist'; ··· 6 6 7 7 // Component to protect routes that require authentication 8 8 const ProtectedRoute = ({ children }) => { 9 - const { isAuthenticated, loading, session, checkAuthStatus } = useAuth(); 9 + const { isAuthenticated, loading, session } = useAuth(); 10 10 const location = useLocation(); 11 - const [redirecting, setRedirecting] = useState(false); 12 - const [checkingStatus, setCheckingStatus] = useState(false); 13 - const checkCount = useRef(0); 14 - const maxChecks = 3; // Maximum number of checks to prevent infinite loops 15 - 16 - // Perform an immediate auth check when the component mounts 17 - useEffect(() => { 18 - const checkAuth = async () => { 19 - if (checkCount.current >= maxChecks) { 20 - console.error("Maximum auth check attempts reached. Stopping to prevent infinite loop."); 21 - return; 22 - } 23 - 24 - // Only proceed if not already checking, not already redirecting, and not loading 25 - if (!isAuthenticated && !checkingStatus && !redirecting && !loading) { 26 - try { 27 - console.log("ProtectedRoute: Checking authentication status"); 28 - setCheckingStatus(true); 29 - checkCount.current += 1; 30 - await checkAuthStatus(); 31 - } catch (error) { 32 - console.error("ProtectedRoute: Auth check failed:", error); 33 - } finally { 34 - setCheckingStatus(false); 35 - } 36 - } 37 - }; 38 - 39 - // Call immediately on mount or when dependency values change 40 - checkAuth(); 41 - 42 - // Set up interval for periodic checks only if authenticated 43 - let interval; 44 - if (isAuthenticated && session) { 45 - console.log("ProtectedRoute: Setting up periodic auth checks"); 46 - interval = setInterval(() => { 47 - checkAuthStatus().catch(err => { 48 - console.error("Error in periodic auth check:", err); 49 - }); 50 - }, 30000); // Check every 30 seconds 51 - } 52 - 53 - return () => { 54 - if (interval) { 55 - console.log("ProtectedRoute: Clearing periodic auth checks"); 56 - clearInterval(interval); 57 - } 58 - }; 59 - }, [isAuthenticated, checkAuthStatus, redirecting, loading, checkingStatus, session]); 60 11 61 - // Show loading state while authentication is being checked 62 - if (loading || checkingStatus) { 63 - return <Loading message="Checking authentication..." />; 12 + // Show loading state while authentication is being determined by AuthProvider 13 + if (loading) { 14 + console.log("ProtectedRoute: Waiting for AuthProvider loading..."); 15 + return <Loading message="Loading authentication status..." />; 64 16 } 65 17 66 - // If not authenticated, redirect to login with return URL 67 - if (!isAuthenticated && !redirecting) { 68 - console.log("ProtectedRoute: Not authenticated, redirecting to login"); 69 - setRedirecting(true); // Prevent multiple redirects 70 - const returnUrl = encodeURIComponent(location.pathname); 18 + // If not authenticated after loading, redirect to login with return URL 19 + if (!isAuthenticated) { 20 + console.log("ProtectedRoute: Not authenticated after loading, redirecting to login"); 21 + const returnUrl = encodeURIComponent(location.pathname + location.search); 71 22 return <Navigate to={`/login?returnUrl=${returnUrl}`} replace />; 72 23 } 73 24 74 - // Check if user is allowed 25 + // Check if user is allowed (allowlist check remains) 75 26 if (session && session.handle && !isAccountAllowed(session)) { 76 27 console.log("ProtectedRoute: User not in allowlist, redirecting to supporter page"); 77 28 return <Navigate to="/supporter" replace />; 78 29 } 79 30 80 - // Reset counter when rendering the protected content 81 - checkCount.current = 0; 82 - 83 31 // Render children if authenticated and allowed 84 - console.log("ProtectedRoute: Authentication successful, rendering protected content"); 32 + console.log(`ProtectedRoute: Authentication successful (${session?.handle}), rendering protected content for ${location.pathname}`); 85 33 return children; 86 34 }; 87 35