mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Set up infinite loading for notebooks (#49321)
## Context Sets up infinite loading for notebooks with the `InfiniteListDefault` component Also adds the notebook and chats count on the explorer home nav <img width="275" height="137" alt="image" src="https://github.com/user-attachments/assets/c3f16e8f-f520-4107-a188-43b1abcca043" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Explorer navigation now displays accurate notebook and chat counts. * Notebook lists support infinite scrolling, loading indicators, and improved active-state styling. * Notebook navigation remains available as additional items load. * **Bug Fixes** * Corrected default markdown cell formatting by removing unintended leading spaces from headings and notes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
This commit is contained in:
1 parent
6779bf52e1
commit
9a3500aad2
3 files changed
+73
-20
No files matched your search
@@ -40,8 +40,9 @@ export const createLogCellSkeleton = ({
|
||||
}
|
||||
|
||||
const DEFAULT_MARKDOWN_CONTENT = `
|
||||
# New section
|
||||
Add notes about your queries and results
|
||||
# New section
|
||||
|
||||
Add notes about your queries and results
|
||||
`.trim()
|
||||
|
||||
export const createMarkdownCellSkeleton = ({
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from './ExplorerLayout.constants'
|
||||
import { formatRelativeTimeShort, getRecentlyUpdatedItems } from './ExplorerNavHome.utils'
|
||||
import { useCreateChat } from '@/components/interfaces/Explorer/hooks'
|
||||
import { useContentCountQuery } from '@/data/content/content-count-query'
|
||||
import { useNotebooksInfiniteQuery } from '@/data/content/notebooks/notebooks-infinite-query'
|
||||
import { useAiAssistantChatList } from '@/state/ai-assistant-state'
|
||||
|
||||
@@ -27,6 +28,10 @@ export const ExplorerNavHome = ({
|
||||
const notebooks = notebooksData?.pages.flatMap((page) => page.content) ?? []
|
||||
const chats = useAiAssistantChatList()
|
||||
|
||||
const { data: notebookCountData } = useContentCountQuery({ projectRef: ref, type: 'notebook' })
|
||||
// [Joshen] Notebooks are all shared by default, none private
|
||||
const notebookCount = notebookCountData?.shared ?? 0
|
||||
|
||||
const recentItems = getRecentlyUpdatedItems({ notebooks, chats })
|
||||
|
||||
return (
|
||||
@@ -52,7 +57,9 @@ export const ExplorerNavHome = ({
|
||||
>
|
||||
<Icon size={14} className="shrink-0" />
|
||||
<span className="flex-1 text-left">{label}</span>
|
||||
<span className="text-xs text-foreground-lighter">{/* Length will be here */}</span>
|
||||
<span className="text-xs text-foreground-lighter">
|
||||
{type === 'notebook' ? notebookCount : chats.length}
|
||||
</span>
|
||||
<ChevronRight size={14} className="shrink-0 text-foreground-muted" />
|
||||
</button>
|
||||
)
|
||||
|
||||
@@ -7,23 +7,69 @@ import { cn } from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { ExplorerNavResourceWrapper, rowClassName } from './ExplorerLayout.constants'
|
||||
import { useNotebooksInfiniteQuery } from '@/data/content/notebooks/notebooks-infinite-query'
|
||||
import {
|
||||
InfiniteListDefault,
|
||||
LoaderForIconMenuItems,
|
||||
type RowComponentBaseProps,
|
||||
} from '@/components/ui/InfiniteList'
|
||||
import {
|
||||
NotebookRow,
|
||||
useNotebooksInfiniteQuery,
|
||||
} from '@/data/content/notebooks/notebooks-infinite-query'
|
||||
|
||||
const NOTEBOOK_ROW_HEIGHT = 28
|
||||
|
||||
type NotebookListItemProps = RowComponentBaseProps<NotebookRow> & {
|
||||
projectRef: string | undefined
|
||||
activeNotebookId: string | undefined
|
||||
}
|
||||
|
||||
const NotebookListItem = ({
|
||||
item: notebook,
|
||||
style,
|
||||
projectRef,
|
||||
activeNotebookId,
|
||||
}: NotebookListItemProps) => {
|
||||
const isActive = activeNotebookId === notebook.id
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`/project/${projectRef}/explorer/notebook/${notebook.id}`}
|
||||
className={rowClassName(isActive)}
|
||||
style={style}
|
||||
>
|
||||
<NotebookText size={14} className={cn('shrink-0', isActive && 'text-foreground')} />
|
||||
<span className="truncate text-left">{notebook.name}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export const ExplorerNavNotebooks = ({ onBack }: { onBack: () => void }) => {
|
||||
const router = useRouter()
|
||||
const { ref, id } = useParams()
|
||||
const [search, setSearch] = useState('')
|
||||
|
||||
const { data: notebooksData, isPending } = useNotebooksInfiniteQuery({
|
||||
const {
|
||||
data: notebooksData,
|
||||
isPending,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
} = useNotebooksInfiniteQuery({
|
||||
projectRef: ref,
|
||||
limit: 100,
|
||||
name: search,
|
||||
})
|
||||
|
||||
const notebooks = useMemo(() => {
|
||||
const items = notebooksData?.pages.flatMap((page) => page.content) ?? []
|
||||
return items
|
||||
}, [notebooksData?.pages])
|
||||
|
||||
const activeNotebookId = router.pathname.includes('/explorer/notebook/') ? id : undefined
|
||||
|
||||
const itemProps = useMemo(() => ({ projectRef: ref, activeNotebookId }), [ref, activeNotebookId])
|
||||
|
||||
return (
|
||||
<ExplorerNavResourceWrapper
|
||||
type="notebook"
|
||||
@@ -31,7 +77,7 @@ export const ExplorerNavNotebooks = ({ onBack }: { onBack: () => void }) => {
|
||||
setSearch={setSearch}
|
||||
onBack={onBack}
|
||||
>
|
||||
<div className="flex flex-1 flex-col gap-px overflow-y-auto px-3 pb-3">
|
||||
<div className="flex flex-1 min-h-0 flex-col px-3 pb-3">
|
||||
{isPending ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : notebooks.length === 0 ? (
|
||||
@@ -39,20 +85,19 @@ export const ExplorerNavNotebooks = ({ onBack }: { onBack: () => void }) => {
|
||||
{search ? 'No notebooks found' : 'No notebooks created yet'}
|
||||
</p>
|
||||
) : (
|
||||
notebooks.map((notebook) => {
|
||||
const isActive = router.pathname.includes('/explorer/notebook/') && id === notebook.id
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={notebook.id}
|
||||
href={`/project/${ref}/explorer/notebook/${notebook.id}`}
|
||||
className={rowClassName(isActive)}
|
||||
>
|
||||
<NotebookText size={14} className={cn('shrink-0', isActive && 'text-foreground')} />
|
||||
<span className="truncate text-left">{notebook.name}</span>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
<InfiniteListDefault
|
||||
className="h-full w-full"
|
||||
items={notebooks}
|
||||
itemProps={itemProps}
|
||||
ItemComponent={NotebookListItem}
|
||||
LoaderComponent={LoaderForIconMenuItems}
|
||||
getItemKey={(index) => notebooks[index]?.id ?? `notebook-${index}`}
|
||||
getItemSize={() => NOTEBOOK_ROW_HEIGHT}
|
||||
gap={1}
|
||||
hasNextPage={hasNextPage}
|
||||
isLoadingNextPage={isFetchingNextPage}
|
||||
onLoadNextPage={fetchNextPage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ExplorerNavResourceWrapper>
|
||||
|
||||
Reference in new issue
Block a user