Skip to content

Commit 04fed23

Browse files
committed
Add loading indicators for graph fetching and options loading
- 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.
1 parent 5bf503e commit 04fed23

4 files changed

Lines changed: 85 additions & 49 deletions

File tree

app/components/code-graph.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
22
import { Graph, GraphData, Node, Link } from "./model";
33
import { Toolbar } from "./toolbar";
44
import { Labels } from "./labels";
5-
import { Download, GitFork, Search, X } from "lucide-react";
5+
import { Download, GitFork, Loader2, Search, X } from "lucide-react";
66
import ElementMenu from "./elementMenu";
77
import Combobox from "./combobox";
88
import { toast } from '@/components/ui/use-toast';
@@ -22,6 +22,7 @@ interface Props {
2222
data: GraphData,
2323
setData: Dispatch<SetStateAction<GraphData>>,
2424
onFetchGraph: (graphName: string) => Promise<void>,
25+
isFetchingGraph: boolean,
2526
onFetchNode: (nodeIds: number[]) => Promise<GraphData>,
2627
options: string[]
2728
setOptions: Dispatch<SetStateAction<string[]>>
@@ -49,9 +50,8 @@ export function CodeGraph({
4950
data,
5051
setData,
5152
onFetchGraph,
53+
isFetchingGraph,
5254
onFetchNode,
53-
options,
54-
setOptions,
5555
isShowPath,
5656
setPath,
5757
chartRef,
@@ -82,6 +82,7 @@ export function CodeGraph({
8282
const [commitIndex, setCommitIndex] = useState<number>(0);
8383
const [currentCommit, setCurrentCommit] = useState(0);
8484
const containerRef = useRef<HTMLDivElement>(null);
85+
const [options, setOptions] = useState<string[]>([]);
8586

8687
useEffect(() => {
8788
setData({ ...graph.Elements })
@@ -370,10 +371,18 @@ export function CodeGraph({
370371
</div>
371372
</div>
372373
</div>
373-
: <div className="flex flex-col items-center justify-center h-full text-gray-400">
374-
<GitFork className="md:w-24 md:h-24 w-16 h-16" color="gray" />
375-
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
376-
</div>
374+
: (
375+
isFetchingGraph ?
376+
<div className="flex flex-col items-center justify-center h-full text-gray-400">
377+
<Loader2 className="md:w-24 md:h-24 w-16 h-16 animate-spin" color="gray" />
378+
<h1 className="md:text-4xl text-2xl text-center">Fetching graph...</h1>
379+
</div>
380+
:
381+
<div className="flex flex-col items-center justify-center h-full text-gray-400">
382+
<GitFork className="md:w-24 md:h-24 w-16 h-16" color="gray" />
383+
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
384+
</div>
385+
)
377386
}
378387
</main>
379388
{/* {

app/components/combobox.tsx

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
22
import { toast } from "@/components/ui/use-toast";
3+
import { Loader2 } from "lucide-react";
34
import { useEffect, useState } from "react";
45

56
interface Props {
@@ -13,55 +14,75 @@ interface Props {
1314
export default function Combobox({ options, setOptions, selectedValue, onSelectedValue }: Props) {
1415

1516
const [open, setOpen] = useState(false)
16-
const [lastOpened, setLastOpened] = useState<number>();
17+
const [lastFetch, setLastFetch] = useState<number>();
18+
const [isFetchingOptions, setIsFetchingOptions] = useState(false)
1719

1820
const fetchOptions = async () => {
19-
const result = await fetch(`/api/repo`, {
20-
method: 'GET',
21-
})
21+
setIsFetchingOptions(true)
2222

23-
if (!result.ok) {
24-
toast({
25-
variant: "destructive",
26-
title: "Uh oh! Something went wrong.",
27-
description: await result.text(),
23+
try {
24+
const result = await fetch(`/api/repo`, {
25+
method: 'GET',
2826
})
29-
return
30-
}
3127

32-
const json = await result.json()
33-
setOptions(json.result)
28+
if (!result.ok) {
29+
toast({
30+
variant: "destructive",
31+
title: "Uh oh! Something went wrong.",
32+
description: await result.text(),
33+
})
34+
return
35+
}
36+
37+
const json = await result.json()
38+
setOptions(json.result)
39+
} finally {
40+
setIsFetchingOptions(false)
41+
}
3442
}
3543

3644
useEffect(() => {
3745
fetchOptions()
3846
}, [])
3947

48+
//fetch options when the combobox is opened
4049
useEffect(() => {
4150
if (!open) return
4251

4352
const now = Date.now();
4453

45-
if (lastOpened && now - lastOpened < 30000) return;
46-
47-
setLastOpened(now);
48-
54+
//check if last fetch was less than 30 seconds ago
55+
if (lastFetch && now - lastFetch < 30000) return;
56+
57+
setLastFetch(now);
58+
4959
fetchOptions()
5060
}, [open])
5161

5262
return (
53-
<Select open={open} onOpenChange={setOpen} value={selectedValue} onValueChange={onSelectedValue}>
63+
<Select open={open} onOpenChange={setOpen} value={isFetchingOptions ? "Fetching options..." : options.length !== 0 ? selectedValue : "No options found"} onValueChange={onSelectedValue}>
5464
<SelectTrigger className="z-10 md:z-0 rounded-md border border-gray-400 md:border-gray-100 focus:ring-0 focus:ring-offset-0">
5565
<SelectValue placeholder="Select a repo" />
5666
</SelectTrigger>
5767
<SelectContent>
5868
{
59-
options.length !== 0 &&
60-
options.map((option) => (
61-
<SelectItem key={option} value={option}>
62-
{option}
69+
isFetchingOptions ?
70+
<SelectItem value="Fetching options...">
71+
<div className="flex flex-row items-center gap-2">
72+
<Loader2 className="w-4 h-4 animate-spin" />
73+
<p>Fetching options...</p>
74+
</div>
6375
</SelectItem>
64-
))
76+
: options.length !== 0 ?
77+
options.map((option) => (
78+
<SelectItem key={option} value={option}>
79+
{option}
80+
</SelectItem>
81+
))
82+
:
83+
<SelectItem value="No options found">
84+
<p>No options found</p>
85+
</SelectItem>
6586
}
6687
</SelectContent>
6788
</Select>

app/components/graphView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,6 @@ export default function GraphView({
352352
onZoom={() => unsetSelectedObjects()}
353353
onEngineStop={() => {
354354
setCooldownTicks(0)
355-
debugger
356355
handleZoomToFit(chartRef, zoomedNodes.length === 1 ? 4 : 1, (n: NodeObject<Node>) => zoomedNodes.some(node => node.id === n.id))
357356
setZoomedNodes([])
358357
}}

app/page.tsx

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export default function Home() {
7979
const [activeIndex, setActiveIndex] = useState(0)
8080
const [carouselApi, setCarouselApi] = useState<CarouselApi>()
8181
const [zoomedNodes, setZoomedNodes] = useState<Node[]>([])
82+
const [isFetchingGraph, setIsFetchingGraph] = useState(false)
8283

8384
useEffect(() => {
8485
if (path?.start?.id && path?.end?.id) {
@@ -138,27 +139,31 @@ export default function Home() {
138139
async function onFetchGraph(graphName: string) {
139140

140141
setGraph(Graph.empty())
142+
setIsFetchingGraph(true)
143+
try {
144+
const result = await fetch(`/api/repo/${prepareArg(graphName)}`, {
145+
method: 'GET'
146+
})
141147

142-
const result = await fetch(`/api/repo/${prepareArg(graphName)}`, {
143-
method: 'GET'
144-
})
148+
if (!result.ok) {
149+
toast({
150+
variant: "destructive",
151+
title: "Uh oh! Something went wrong.",
152+
description: await result.text(),
153+
})
154+
return
155+
}
145156

146-
if (!result.ok) {
147-
toast({
148-
variant: "destructive",
149-
title: "Uh oh! Something went wrong.",
150-
description: await result.text(),
151-
})
152-
return
157+
const json = await result.json()
158+
const g = Graph.create(json.result.entities, graphName)
159+
setGraph(g)
160+
setIsPathResponse(false)
161+
chatPanel.current?.expand()
162+
// @ts-ignore
163+
window.graph = g
164+
} finally {
165+
setIsFetchingGraph(false)
153166
}
154-
155-
const json = await result.json()
156-
const g = Graph.create(json.result.entities, graphName)
157-
setGraph(g)
158-
setIsPathResponse(false)
159-
chatPanel.current?.expand()
160-
// @ts-ignore
161-
window.graph = g
162167
}
163168

164169
// Send the user query to the server to expand a node
@@ -391,6 +396,7 @@ export default function Home() {
391396
options={options}
392397
setOptions={setOptions}
393398
onFetchGraph={onFetchGraph}
399+
isFetchingGraph={isFetchingGraph}
394400
onFetchNode={onFetchNode}
395401
setPath={setPath}
396402
isShowPath={!!path}
@@ -509,6 +515,7 @@ export default function Home() {
509515
options={options}
510516
setOptions={setOptions}
511517
onFetchGraph={onFetchGraph}
518+
isFetchingGraph={isFetchingGraph}
512519
onFetchNode={onFetchNode}
513520
setPath={setPath}
514521
isShowPath={!!path}

0 commit comments

Comments
 (0)