diff --git a/app/src/App.tsx b/app/src/App.tsx index 156b1313..17ab27db 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -71,7 +71,6 @@ export default function App() { const [createURL, setCreateURL] = useState("") const [createOpen, setCreateOpen] = useState(false) const [tipOpen, setTipOpen] = useState(false) - const [options, setOptions] = useState([]); const [path, setPath] = useState(); const [isSubmit, setIsSubmit] = useState(false); const desktopChartRef = useRef(null) @@ -90,6 +89,7 @@ export default function App() { const [carouselApi, setCarouselApi] = useState() const [zoomedNodes, setZoomedNodes] = useState([]) const [hasHiddenElements, setHasHiddenElements] = useState(false); + const [isFetchingGraph, setIsFetchingGraph] = useState(false) useEffect(() => { if (path?.start?.id && path?.end?.id) { @@ -140,7 +140,6 @@ export default function App() { const graphName = createURL.split('/').pop()! - setOptions(prev => [...prev, graphName]) setSelectedValue(graphName) setCreateURL("") setCreateOpen(false) @@ -153,6 +152,7 @@ export default function App() { } async function onFetchGraph(graphName: string) { + setIsFetchingGraph(true) try { const result = await fetch(`/api/graph_entities?repo=${prepareArg(graphName)}`, { method: 'GET', @@ -186,6 +186,8 @@ export default function App() { title: "Uh oh! Something went wrong.", description: "Failed to load repository graph. Please try again.", }) + } finally { + setIsFetchingGraph(false) } } @@ -524,9 +526,8 @@ export default function App() { data={data} setData={setData} canvasRef={desktopChartRef} - options={options} - setOptions={setOptions} onFetchGraph={onFetchGraph} + isFetchingGraph={isFetchingGraph} onFetchNode={onFetchNode} setPath={setPath} isShowPath={!!path} @@ -651,9 +652,8 @@ export default function App() { data={data} setData={setData} canvasRef={mobileChartRef} - options={options} - setOptions={setOptions} onFetchGraph={onFetchGraph} + isFetchingGraph={isFetchingGraph} onFetchNode={onFetchNode} setPath={setPath} isShowPath={!!path} diff --git a/app/src/components/code-graph.tsx b/app/src/components/code-graph.tsx index c0b2a083..e2b9a911 100644 --- a/app/src/components/code-graph.tsx +++ b/app/src/components/code-graph.tsx @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; import { Graph, GraphData, Node, Link } from "./model"; import { Toolbar } from "./toolbar"; import { Labels } from "./labels"; -import { Download, GitFork, Search, X } from "lucide-react"; +import { GitFork, Loader2, Search, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import ElementMenu from "./elementMenu"; import Combobox from "./combobox"; @@ -28,9 +28,8 @@ interface Props { data: GraphData, setData: Dispatch>, onFetchGraph: (graphName: string) => Promise, + isFetchingGraph: boolean, onFetchNode: (nodeIds: number[]) => Promise, - options: string[] - setOptions: Dispatch> isShowPath: boolean setPath: Dispatch> canvasRef: GraphRef @@ -58,9 +57,8 @@ export function CodeGraph({ data, setData, onFetchGraph, + isFetchingGraph, onFetchNode, - options, - setOptions, isShowPath, setPath, canvasRef, @@ -93,6 +91,7 @@ export function CodeGraph({ const [commitIndex, setCommitIndex] = useState(0); const [currentCommit, setCurrentCommit] = useState(0); const containerRef = useRef(null); + const [options, setOptions] = useState([]); useEffect(() => { setData({ ...graph.Elements }) @@ -516,10 +515,18 @@ export function CodeGraph({ - :
- -

Select a repo to show its graph here

-
+ : ( + isFetchingGraph ? +
+ +

Fetching graph...

+
+ : +
+ +

Select a repo to show its graph here

+
+ ) } {/* { diff --git a/app/src/components/combobox.tsx b/app/src/components/combobox.tsx index 93c6720c..dbf751ee 100644 --- a/app/src/components/combobox.tsx +++ b/app/src/components/combobox.tsx @@ -1,5 +1,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { toast } from "@/components/ui/use-toast"; +import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; const AUTH_HEADERS: HeadersInit = import.meta.env.VITE_SECRET_TOKEN @@ -17,58 +18,83 @@ interface Props { export default function Combobox({ options, setOptions, selectedValue, onSelectedValue }: Props) { const [open, setOpen] = useState(false) - const [lastOpened, setLastOpened] = useState(); + const [lastFetch, setLastFetch] = useState(); + const [isFetchingOptions, setIsFetchingOptions] = useState(false) const fetchOptions = async () => { - const result = await fetch(`/api/list_repos`, { - method: 'GET', - headers: { - ...AUTH_HEADERS, - }, - }) + setIsFetchingOptions(true) - if (!result.ok) { + 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) + setLastFetch(Date.now()) + } catch (error) { toast({ variant: "destructive", title: "Uh oh! Something went wrong.", - description: await result.text(), + description: error instanceof Error ? error.message : "Failed to fetch repositories.", }) - return + } finally { + setIsFetchingOptions(false) } - - const json = await result.json() - setOptions(json.repositories) } useEffect(() => { fetchOptions() }, []) + //fetch options when the combobox is opened useEffect(() => { if (!open) return const now = Date.now(); - if (lastOpened && now - lastOpened < 30000) return; - - setLastOpened(now); + //check if last fetch was less than 30 seconds ago + if (lastFetch && now - lastFetch < 30000) return; fetchOptions() }, [open]) return ( - - + { - options.length !== 0 && - options.map((option) => ( - - {option} + isFetchingOptions ? + +
+ +

Fetching options...

+
- )) + : options.length !== 0 ? + options.map((option) => ( + + {option} + + )) + : + +

No options found

+
}