Skip to content

Fix #426 Add loading states#427

Open
Anchel123 wants to merge 6 commits intostagingfrom
add-loading-states
Open

Fix #426 Add loading states#427
Anchel123 wants to merge 6 commits intostagingfrom
add-loading-states

Conversation

@Anchel123
Copy link
Contributor

@Anchel123 Anchel123 commented Mar 26, 2025

PR Type

Enhancement, Bug fix


Description

  • Added loading indicators for graph fetching and options loading.

    • Introduced isFetchingGraph state for graph loading.
    • Updated CodeGraph to display a spinner during graph fetching.
    • Enhanced Combobox to show a loading state when fetching options.
  • Improved Combobox behavior:

    • Disabled selection when no options are available and not fetching.
    • Added a spinner and message for fetching options.
  • Fixed minor issues and improved user experience:

    • Removed unnecessary debugger statement in GraphView.
    • Enhanced error handling for API fetch failures.

Changes walkthrough 📝

Relevant files
Enhancement
code-graph.tsx
Added loading state and spinner for graph fetching             

app/components/code-graph.tsx

  • Added isFetchingGraph state to manage graph loading.
  • Displayed a spinner when fetching the graph.
  • Updated UI to handle empty graph state more gracefully.
  • +16/-7   
    combobox.tsx
    Enhanced combobox with loading state and error handling   

    app/components/combobox.tsx

  • Added isFetchingOptions state for options loading.
  • Disabled combobox when no options are available.
  • Displayed spinner and messages during options fetching.
  • Improved API fetch error handling.
  • +44/-23 
    page.tsx
    Added graph loading state and improved error handling       

    app/page.tsx

  • Added isFetchingGraph state to manage graph loading.
  • Enhanced error handling for graph fetching API calls.
  • Updated CodeGraph component to use the new loading state.
  • +25/-18 
    Bug fix
    graphView.tsx
    Minor cleanup and zoom behavior improvement                           

    app/components/graphView.tsx

  • Removed unnecessary debugger statement.
  • Improved zoom behavior by resetting zoomed nodes.
  • +0/-1     

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • PR Summary by Typo

    Overview

    This PR addresses issue #426 by implementing loading states across the application, enhancing user experience during data fetching for graph visualizations and repository selection.

    Key Changes

    • Introduced isFetchingGraph state to display a loading spinner and message while fetching graph data in CodeGraph.tsx.
    • Added isFetchingOptions state and a loading spinner to the repository Combobox.tsx when fetching available repositories, along with a 30-second caching mechanism.
    • Disabled the combobox when options are being fetched or none are found.
    • Integrated the isFetchingGraph state into page.tsx to manage the loading status for graph data.
    • Removed an unnecessary debugger statement from graphView.tsx.

    Work Breakdown

    Category Lines Changed
    New Work 53 (52.0%)
    Churn 17 (16.7%)
    Refactor 32 (31.4%)
    Total Changes 102
    To turn off PR summary, please visit Notification settings.

    Summary by CodeRabbit

    • New Features

      • Loading spinners now display when fetching graph data, providing clear visual feedback during in-progress operations.
      • Loading indicators appear when fetching available repository options from the system.
    • Improvements

      • Repository option fetches are throttled to prevent excessive requests, occurring at most once every 30 seconds.
      • Enhanced overall UI responsiveness and user feedback during all data loading operations.

    - Introduced `isFetchingGraph` state to manage loading state in the graph component.
    - Updated `CodeGraph` to display a loading spinner while fetching the graph.
    - Enhanced `Combobox` to show a loading state when fetching options.
    - Updated the Combobox component to disable the select input when there are no options and not fetching options, improving user experience.
    @vercel
    Copy link

    vercel bot commented Mar 26, 2025

    The latest updates on your projects. Learn more about Vercel for GitHub.

    Project Deployment Review Updated (UTC)
    code-graph Error Error Dec 22, 2025 2:36pm

    @coderabbitai
    Copy link
    Contributor

    coderabbitai bot commented Mar 26, 2025

    📝 Walkthrough

    Walkthrough

    The changes introduce a new fetching state tracking system for graph operations in the App component, propagating it to CodeGraph to display loading indicators. Additionally, the Combobox component implements throttled repository option fetching with loading visuals and improved error handling.

    Changes

    Cohort / File(s) Summary
    Graph Fetch State Management
    app/src/App.tsx, app/src/components/code-graph.tsx
    Introduces isFetchingGraph state in App to track ongoing graph fetches, passed as a prop to CodeGraph. CodeGraph now displays a loading indicator (Loader2) when isFetchingGraph is true and manages options state locally instead of receiving it as props.
    Repository Options Fetching
    app/src/components/combobox.tsx
    Adds throttled option fetching (30-second intervals), loading state management (isFetchingOptions), visual loading indicators (Loader2 spinner with disabled select), improved error handling via toast notifications, and dedicated UI states for loading and empty options.

    Estimated code review effort

    🎯 3 (Moderate) | ⏱️ ~20 minutes

    Poem

    A loading spinner spins with delight, ✨
    As graphs and options fetch through the night,
    With throttled requests at a measured pace,
    And states well-tracked in each proper place,
    The UI feels smooth, the errors contained,
    User experience greatly maintained! 🐰

    🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
    Check name Status Explanation
    Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
    Title check ✅ Passed The title accurately summarizes the main change: adding loading states across the application, which aligns with the core modifications to App.tsx, code-graph.tsx, and combobox.tsx.

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

    ✨ Finishing Touches
    • 📝 Generate docstrings (stacked PR)
    • 📝 Generate docstrings (commit on current branch)
    🧪 Generate unit tests (beta)
    • Create PR with unit tests
    • Post copyable unit tests in a comment
    • Commit unit tests in branch add-loading-states
    📝 Coding Plan
    • Generate coding plan for human review comments

    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.

    @Anchel123 Anchel123 requested a review from barakb March 26, 2025 12:48
    @Anchel123 Anchel123 linked an issue Mar 26, 2025 that may be closed by this pull request
    @qodo-code-review
    Copy link
    Contributor

    Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Prop Inconsistency

    The component signature has changed with options/setOptions being moved from props to internal state, but the component is still being called with these props in page.tsx.

    const [options, setOptions] = useState<string[]>([]);
    
    Error Handling

    The error handling in fetchOptions doesn't properly handle network errors (like connection failures) which could cause the loading state to remain indefinitely.

    try {
        const result = await fetch(`/api/repo`, {
            method: 'GET',
        })
    
        if (!result.ok) {
            toast({
                variant: "destructive",
                title: "Uh oh! Something went wrong.",
                description: await result.text(),
            })
            return 
        }
    
        const json = await result.json()
        setOptions(json.result)
    } finally {
        setIsFetchingOptions(false)
    }
    

    @qodo-code-review
    Copy link
    Contributor

    qodo-code-review bot commented Mar 26, 2025

    Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix parameter mismatch

    The function parameters don't match the Props interface. The options and
    setOptions parameters are missing in the function signature but are used later
    in the component. This mismatch could cause runtime errors.

    app/components/code-graph.tsx [48-58]

     export function CodeGraph({
         graph,
         data,
         setData,
         onFetchGraph,
         isFetchingGraph,
         onFetchNode,
    +    options,
    +    setOptions,
         isShowPath,
         setPath,
         chartRef,
         selectedValue,
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    __

    Why: The function signature is missing the options and setOptions parameters that are defined in the Props interface and used in the component. This mismatch would cause runtime errors when the component tries to access these undefined props.

    High
    Remove duplicate state definition

    This component is redefining options and setOptions as local state, but these
    are also passed as props. This creates a conflict where the local state shadows
    the props, making the component ignore the props values and potentially causing
    inconsistent behavior.

    app/components/code-graph.tsx [85]

    -const [options, setOptions] = useState<string[]>([]);
    +// Remove this line as options and setOptions should be used from props
    +// const [options, setOptions] = useState<string[]>([]);
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    __

    Why: The component defines local state for options and setOptions while also receiving them as props, creating a conflict where the local state shadows the props. This would cause the component to ignore the props values, breaking expected behavior.

    Medium
    Fix invalid select value

    The value prop is using string literals like "Fetching options..." and "No
    options found" which might not exist in the options array. This can cause React
    Select to have an invalid state where the selected value doesn't match any
    available option, potentially causing rendering issues or unexpected behavior.

    app/components/combobox.tsx [63]

    -<Select open={open} onOpenChange={setOpen} disabled={options.length === 0 && !isFetchingOptions} value={isFetchingOptions ? "Fetching options..." : options.length !== 0 ? selectedValue : "No options found"} onValueChange={onSelectedValue}>
    +<Select 
    +  open={open} 
    +  onOpenChange={setOpen} 
    +  disabled={options.length === 0 && !isFetchingOptions} 
    +  value={options.includes(selectedValue) ? selectedValue : ""} 
    +  onValueChange={onSelectedValue}
    +>
    • Apply this suggestion
    Suggestion importance[1-10]: 6

    __

    Why: The current implementation uses string literals as values that don't exist in the options array, which can cause React Select to have an invalid state. The improved code ensures the value is always valid by checking if the selectedValue exists in options.

    Low
    • Update

    AviAvni
    AviAvni previously approved these changes Apr 3, 2025
    @typo-app
    Copy link

    typo-app bot commented Dec 22, 2025

    Static Code Review 📊

    ✅ All quality checks passed!

    Copy link

    @typo-app typo-app bot left a comment

    Choose a reason for hiding this comment

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

    AI Code Review 🤖

    Files Reviewed: 4
    Comments Added: 1
    Lines of Code Analyzed: 134
    Critical Issues: 0

    PR Health: Excellent 🔥

    Give 👍 or 👎 on each review comment to help us improve.

    Resolve merge conflicts:
    - app/components/graphView.tsx: accept deletion (debugger fix already in staging)
    - app/page.tsx: accept deletion, apply isFetchingGraph to app/src/App.tsx
    - app/src/components/code-graph.tsx: merge Loader2+Button imports, add loading spinner
    - app/src/components/combobox.tsx: merge loading state with staging API/headers
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    …tion or class'
    
    Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
    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

    🧹 Nitpick comments (1)
    app/src/components/combobox.tsx (1)

    61-66: Throttle timestamp set before async fetch completes.

    setLastFetch(now) is called before fetchOptions() starts. If the fetch fails, users must still wait 30 seconds before retrying. Consider moving setLastFetch into the try block after a successful fetch, or into finally, so failed fetches don't block immediate retries.

    ♻️ Proposed fix
             //check if last fetch was less than 30 seconds ago
             if (lastFetch && now - lastFetch < 30000) return;
    -        
    -        setLastFetch(now);
    -        
    -        fetchOptions()
    +
    +        fetchOptions().then(() => setLastFetch(now))
         }, [open])

    Or update the timestamp in fetchOptions after success.

    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@app/src/components/combobox.tsx` around lines 61 - 66, The throttle timestamp
    is set before the async fetch completes, which blocks retries if fetchOptions()
    fails; move the setLastFetch(now) call out of the pre-fetch path and instead
    update lastFetch only after a successful fetch (e.g. inside the try after await
    fetchOptions()) or alternatively in a finally block if you want to always record
    attempts; adjust the code around lastFetch, setLastFetch, fetchOptions and now
    so errors don't prevent immediate retry.
    
    🤖 Prompt for all review comments with AI agents
    Verify each finding against the current code and only fix it if needed.
    
    Inline comments:
    In `@app/src/components/combobox.tsx`:
    - Line 70: The Select is allowing placeholder strings to be chosen (causing
    onSelectedValue to receive "Fetching options..." or "No options found"); update
    the Select usage (component named Select with props
    open/onOpenChange/setOpen/disabled/value/onValueChange) to prevent selection of
    placeholders by disabling the control while options are loading or empty (i.e.,
    include isFetchingOptions in the disabled expression), and ensure the value
    passed is undefined/null when showing a placeholder instead of using the
    placeholder text as the selected value; alternatively remove placeholder items
    from the option list so only real options can be selected and onSelectedValue is
    only called for actual option values.
    - Around line 27-48: The fetch call in the async block that populates options
    can throw network errors which are currently swallowed; wrap the await
    fetch(...) and response handling in a try/catch (or add a catch after the
    existing try/finally) to catch exceptions, call toast with a descriptive error
    message (using the caught error.message) and ensure setIsFetchingOptions(false)
    remains in the finally; update the logic around setOptions(...) so it only runs
    on success and reference the existing symbols fetch(`/api/list_repos`),
    setOptions, setIsFetchingOptions, and toast when implementing the catch and
    toast error handling.
    
    ---
    
    Nitpick comments:
    In `@app/src/components/combobox.tsx`:
    - Around line 61-66: The throttle timestamp is set before the async fetch
    completes, which blocks retries if fetchOptions() fails; move the
    setLastFetch(now) call out of the pre-fetch path and instead update lastFetch
    only after a successful fetch (e.g. inside the try after await fetchOptions())
    or alternatively in a finally block if you want to always record attempts;
    adjust the code around lastFetch, setLastFetch, fetchOptions and now so errors
    don't prevent immediate retry.
    

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    Run ID: 21575903-fc11-444c-b409-d821e572aeac

    📥 Commits

    Reviewing files that changed from the base of the PR and between 887b82f and 17706ca.

    📒 Files selected for processing (3)
    • app/src/App.tsx
    • app/src/components/code-graph.tsx
    • app/src/components/combobox.tsx

    Comment on lines +27 to +48
    try {
    const result = await fetch(`/api/list_repos`, {
    method: 'GET',
    headers: {
    ...AUTH_HEADERS,
    },
    })
    return
    }

    const json = await result.json()
    setOptions(json.repositories)
    if (!result.ok) {
    toast({
    variant: "destructive",
    title: "Uh oh! Something went wrong.",
    description: await result.text(),
    })
    return
    }

    const json = await result.json()
    setOptions(json.repositories)
    } finally {
    setIsFetchingOptions(false)
    }
    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

    Network errors are silently swallowed.

    If fetch() throws (e.g., network failure), the finally block clears the loading state, but the user receives no feedback. Consider adding a catch block to display an error toast.

    🛠️ Proposed fix
             try {
                 const result = await fetch(`/api/list_repos`, {
                     method: 'GET',
                     headers: {
                         ...AUTH_HEADERS,
                     },
                 })
    
                 if (!result.ok) {
                     toast({
                         variant: "destructive",
                         title: "Uh oh! Something went wrong.",
                         description: await result.text(),
                     })
                     return 
                 }
    
                 const json = await result.json()
                 setOptions(json.repositories)
    +        } catch (error) {
    +            toast({
    +                variant: "destructive",
    +                title: "Uh oh! Something went wrong.",
    +                description: "Failed to fetch repositories. Please try again.",
    +            })
             } finally {
                 setIsFetchingOptions(false)
             }
    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@app/src/components/combobox.tsx` around lines 27 - 48, The fetch call in the
    async block that populates options can throw network errors which are currently
    swallowed; wrap the await fetch(...) and response handling in a try/catch (or
    add a catch after the existing try/finally) to catch exceptions, call toast with
    a descriptive error message (using the caught error.message) and ensure
    setIsFetchingOptions(false) remains in the finally; update the logic around
    setOptions(...) so it only runs on success and reference the existing symbols
    fetch(`/api/list_repos`), setOptions, setIsFetchingOptions, and toast when
    implementing the catch and toast error handling.
    


    return (
    <Select open={open} onOpenChange={setOpen} value={selectedValue} onValueChange={onSelectedValue}>
    <Select open={open} onOpenChange={setOpen} disabled={options.length === 0 && !isFetchingOptions} value={isFetchingOptions ? "Fetching options..." : options.length !== 0 ? selectedValue : "No options found"} onValueChange={onSelectedValue}>
    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

    Placeholder items are selectable, which may trigger unintended behavior.

    During loading, the Select is enabled and shows a "Fetching options..." item. If the user clicks it, onSelectedValue("Fetching options...") will be called, potentially causing errors or unexpected state in the parent. Similarly for "No options found".

    Consider disabling the Select during loading, or making placeholder items non-interactive:

    🛠️ Proposed fix - disable Select during loading
    -        <Select open={open} onOpenChange={setOpen} disabled={options.length === 0 && !isFetchingOptions} value={isFetchingOptions ? "Fetching options..." : options.length !== 0 ? selectedValue : "No options found"} onValueChange={onSelectedValue}>
    +        <Select open={open} onOpenChange={setOpen} disabled={isFetchingOptions || options.length === 0} value={isFetchingOptions ? undefined : options.length !== 0 ? selectedValue : undefined} onValueChange={onSelectedValue}>

    This disables the Select while fetching, preventing accidental selection of placeholder items.

    Also applies to: 76-82

    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@app/src/components/combobox.tsx` at line 70, The Select is allowing
    placeholder strings to be chosen (causing onSelectedValue to receive "Fetching
    options..." or "No options found"); update the Select usage (component named
    Select with props open/onOpenChange/setOpen/disabled/value/onValueChange) to
    prevent selection of placeholders by disabling the control while options are
    loading or empty (i.e., include isFetchingOptions in the disabled expression),
    and ensure the value passed is undefined/null when showing a placeholder instead
    of using the placeholder text as the selected value; alternatively remove
    placeholder items from the option list so only real options can be selected and
    onSelectedValue is only called for actual option values.
    

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    Add loading states

    3 participants