feat(graph): auto-paginate consumer graph + loading progress chip (#966)

This commit is contained in:
Mahesh Sanikommu 2026-05-18 10:15:30 -07:00 committed by GitHub
parent 39f30f10b2
commit fbd9f5e4cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 4 deletions

View file

@ -46,6 +46,7 @@ export const GraphLayoutView = memo(function GraphLayoutView({
maxNodes={undefined}
canvasRef={canvasRef}
onOpenDocument={onOpenDocument}
autoFetchAll
/>
</div>

View file

@ -1,7 +1,7 @@
"use client"
import { useInfiniteQuery } from "@tanstack/react-query"
import { useMemo } from "react"
import { useEffect, useMemo } from "react"
import { $fetch } from "@lib/api"
import type {
GraphApiDocument,
@ -15,6 +15,7 @@ interface UseGraphApiOptions {
containerTags?: string[]
documentIds?: string[]
enabled?: boolean
autoFetchAll?: boolean
}
interface ApiMemoryEntry {
@ -108,7 +109,12 @@ function toGraphDocument(
}
export function useGraphApi(options: UseGraphApiOptions = {}) {
const { containerTags, documentIds, enabled = true } = options
const {
containerTags,
documentIds,
enabled = true,
autoFetchAll = false,
} = options
const filteredDocumentIds = documentIds?.filter(Boolean)
const hasDocumentIds =
filteredDocumentIds != null && filteredDocumentIds.length > 0
@ -163,6 +169,19 @@ export function useGraphApi(options: UseGraphApiOptions = {}) {
enabled,
})
useEffect(() => {
if (!autoFetchAll || !enabled) return
if (!hasNextPage || isPending || isFetchingNextPage) return
fetchNextPage()
}, [
autoFetchAll,
enabled,
hasNextPage,
isPending,
isFetchingNextPage,
fetchNextPage,
])
const documents = useMemo(() => {
if (!data?.pages) return []
return data.pages.flatMap((page) =>

View file

@ -1,9 +1,11 @@
"use client"
import { useEffect, useRef, useState } from "react"
import { useEffect, useMemo, useRef, useState } from "react"
import { Loader2 } from "lucide-react"
import { MemoryGraph as MemoryGraphBase } from "@supermemory/memory-graph"
import type { GraphThemeColors } from "@supermemory/memory-graph"
import { useGraphApi } from "./hooks/use-graph-api"
import { cn } from "@lib/utils"
export interface MemoryGraphWrapperProps {
children?: React.ReactNode
@ -21,6 +23,7 @@ export interface MemoryGraphWrapperProps {
onSlideshowStop?: () => void
canvasRef?: React.RefObject<HTMLCanvasElement | null>
onOpenDocument?: (documentId: string) => void
autoFetchAll?: boolean
}
export function MemoryGraph({
@ -32,6 +35,7 @@ export function MemoryGraph({
documentIds,
maxNodes,
canvasRef,
autoFetchAll = false,
...rest
}: MemoryGraphWrapperProps) {
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 })
@ -60,10 +64,22 @@ export function MemoryGraph({
containerTags,
documentIds,
enabled: containerSize.width > 0 && containerSize.height > 0,
autoFetchAll,
})
const loadedDocCount = documents.length
const loadedMemoryCount = useMemo(
() => documents.reduce((sum, doc) => sum + doc.memories.length, 0),
[documents],
)
const showProgress =
autoFetchAll &&
!apiIsLoading &&
totalCount > 0 &&
(hasMore || isLoadingMore || loadedDocCount < totalCount)
return (
<div ref={containerRef} className="size-full [&>div]:!bg-none">
<div ref={containerRef} className="relative size-full [&>div]:!bg-none">
<MemoryGraphBase
documents={documents}
isLoading={externalIsLoading || apiIsLoading}
@ -85,6 +101,24 @@ export function MemoryGraph({
>
{children}
</MemoryGraphBase>
{showProgress && (
<div
className={cn(
"absolute right-3 top-3 z-15 md:right-4 md:top-4",
"flex items-center gap-2 rounded-full",
"bg-background/85 px-3 py-1.5 text-xs backdrop-blur-md",
"border border-border/60 shadow-sm",
)}
aria-live="polite"
>
<Loader2 className="size-3 shrink-0 animate-spin text-muted-foreground" />
<span className="tabular-nums">
{loadedMemoryCount.toLocaleString()} memories ·{" "}
{loadedDocCount.toLocaleString()}/{totalCount.toLocaleString()}{" "}
documents
</span>
</div>
)}
</div>
)
}