Skip to content
Open
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
12 changes: 6 additions & 6 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string[]>([]);
const [path, setPath] = useState<Path | undefined>();
const [isSubmit, setIsSubmit] = useState<boolean>(false);
const desktopChartRef = useRef<GraphRef["current"]>(null)
Expand All @@ -90,6 +89,7 @@ export default function App() {
const [carouselApi, setCarouselApi] = useState<CarouselApi>()
const [zoomedNodes, setZoomedNodes] = useState<Node[]>([])
const [hasHiddenElements, setHasHiddenElements] = useState(false);
const [isFetchingGraph, setIsFetchingGraph] = useState(false)

useEffect(() => {
if (path?.start?.id && path?.end?.id) {
Expand Down Expand Up @@ -140,7 +140,6 @@ export default function App() {

const graphName = createURL.split('/').pop()!

setOptions(prev => [...prev, graphName])
setSelectedValue(graphName)
setCreateURL("")
setCreateOpen(false)
Expand All @@ -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',
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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}
Expand Down
25 changes: 16 additions & 9 deletions app/src/components/code-graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -28,9 +28,8 @@ interface Props {
data: GraphData,
setData: Dispatch<SetStateAction<GraphData>>,
onFetchGraph: (graphName: string) => Promise<void>,
isFetchingGraph: boolean,
onFetchNode: (nodeIds: number[]) => Promise<GraphData>,
options: string[]
setOptions: Dispatch<SetStateAction<string[]>>
isShowPath: boolean
setPath: Dispatch<SetStateAction<Path | undefined>>
canvasRef: GraphRef
Expand Down Expand Up @@ -58,9 +57,8 @@ export function CodeGraph({
data,
setData,
onFetchGraph,
isFetchingGraph,
onFetchNode,
options,
setOptions,
isShowPath,
setPath,
canvasRef,
Expand Down Expand Up @@ -93,6 +91,7 @@ export function CodeGraph({
const [commitIndex, setCommitIndex] = useState<number>(0);
const [currentCommit, setCurrentCommit] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
const [options, setOptions] = useState<string[]>([]);

useEffect(() => {
setData({ ...graph.Elements })
Expand Down Expand Up @@ -516,10 +515,18 @@ export function CodeGraph({
</div>
</div>
</div>
: <div className="flex flex-col items-center justify-center h-full text-muted-foreground">
<GitFork className="md:w-24 md:h-24 w-16 h-16" />
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
</div>
: (
isFetchingGraph ?
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
<Loader2 className="md:w-24 md:h-24 w-16 h-16 animate-spin" />
<h1 className="md:text-4xl text-2xl text-center">Fetching graph...</h1>
</div>
:
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
<GitFork className="md:w-24 md:h-24 w-16 h-16" />
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
</div>
)
}
</main>
{/* {
Expand Down
72 changes: 49 additions & 23 deletions app/src/components/combobox.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,58 +18,83 @@ interface Props {
export default function Combobox({ options, setOptions, selectedValue, onSelectedValue }: Props) {

const [open, setOpen] = useState(false)
const [lastOpened, setLastOpened] = useState<number>();
const [lastFetch, setLastFetch] = useState<number>();
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 (
<Select open={open} onOpenChange={setOpen} value={selectedValue} onValueChange={onSelectedValue}>
<Select open={open} onOpenChange={setOpen} disabled={isFetchingOptions || (options.length === 0)} value={selectedValue} onValueChange={onSelectedValue}>
<SelectTrigger className="z-10 md:z-0 rounded-md border border-border focus:ring-1 focus:ring-primary">
<SelectValue placeholder="Select a repo" />
<SelectValue placeholder={isFetchingOptions ? "Fetching options..." : "Select a repo"} />
</SelectTrigger>
<SelectContent>
{
options.length !== 0 &&
options.map((option) => (
<SelectItem key={option} value={option}>
{option}
isFetchingOptions ?
<SelectItem value="Fetching options...">
<div className="flex flex-row items-center gap-2">
<Loader2 className="w-4 h-4 animate-spin" />
<p>Fetching options...</p>
</div>
</SelectItem>
))
: options.length !== 0 ?
options.map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))
:
<SelectItem value="No options found">
<p>No options found</p>
</SelectItem>
}
</SelectContent>
</Select>
Expand Down
Loading