refactor: Refactor online drive breadcrumbs navigation

This commit is contained in:
twwu 2025-08-28 10:56:51 +08:00
parent 4403a26f37
commit 843b14ccc6
8 changed files with 66 additions and 49 deletions

View File

@ -70,7 +70,6 @@ const OnlineDocuments = ({
const getOnlineDocuments = useCallback(async () => {
const { currentCredentialId } = dataSourceStore.getState()
if (!currentCredentialId) return
ssePost(
datasourceNodeRunURL,
{
@ -96,6 +95,7 @@ const OnlineDocuments = ({
}, [dataSourceStore, datasourceNodeRunURL])
useEffect(() => {
if (!currentCredentialId) return
getOnlineDocuments()
}, [currentCredentialId])

View File

@ -3,12 +3,12 @@ import cn from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
type DriveProps = {
prefix: string[]
breadcrumbs: string[]
handleBackToRoot: () => void
}
const Drive = ({
prefix,
breadcrumbs,
handleBackToRoot,
}: DriveProps) => {
const { t } = useTranslation()
@ -19,15 +19,15 @@ const Drive = ({
type='button'
className={cn(
'max-w-full shrink truncate rounded-md px-[5px] py-1',
prefix.length > 0 && 'system-sm-regular text-text-tertiary hover:bg-state-base-hover',
prefix.length === 0 && 'system-sm-medium text-text-secondary',
breadcrumbs.length > 0 && 'system-sm-regular text-text-tertiary hover:bg-state-base-hover',
breadcrumbs.length === 0 && 'system-sm-medium text-text-secondary',
)}
onClick={handleBackToRoot}
disabled={prefix.length === 0}
disabled={breadcrumbs.length === 0}
>
{t('datasetPipeline.onlineDrive.breadcrumbs.allFiles')}
</button>
{prefix.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
{breadcrumbs.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
</>
)
}

View File

@ -7,7 +7,7 @@ import Dropdown from './dropdown'
import Drive from './drive'
type BreadcrumbsProps = {
prefix: string[]
breadcrumbs: string[]
keywords: string
bucket: string
searchResultsLength: number
@ -15,7 +15,7 @@ type BreadcrumbsProps = {
}
const Breadcrumbs = ({
prefix,
breadcrumbs,
keywords,
bucket,
searchResultsLength,
@ -25,52 +25,57 @@ const Breadcrumbs = ({
const dataSourceStore = useDataSourceStore()
const hasBucket = useDataSourceStoreWithSelector(s => s.hasBucket)
const showSearchResult = !!keywords && searchResultsLength > 0
const showBucketListTitle = prefix.length === 0 && hasBucket && bucket === ''
const showBucketListTitle = breadcrumbs.length === 0 && hasBucket && bucket === ''
const displayBreadcrumbNum = useMemo(() => {
const num = isInPipeline ? 2 : 3
return bucket ? num - 1 : num
}, [isInPipeline, bucket])
const breadcrumbs = useMemo(() => {
const prefixToDisplay = prefix.slice(0, displayBreadcrumbNum - 1)
const collapsedBreadcrumbs = prefix.slice(displayBreadcrumbNum - 1, prefix.length - 1)
const breadcrumbsConfig = useMemo(() => {
const prefixToDisplay = breadcrumbs.slice(0, displayBreadcrumbNum - 1)
const collapsedBreadcrumbs = breadcrumbs.slice(displayBreadcrumbNum - 1, breadcrumbs.length - 1)
return {
original: prefix,
needCollapsed: prefix.length > displayBreadcrumbNum,
original: breadcrumbs,
needCollapsed: breadcrumbs.length > displayBreadcrumbNum,
prefixBreadcrumbs: prefixToDisplay,
collapsedBreadcrumbs,
lastBreadcrumb: prefix[prefix.length - 1],
lastBreadcrumb: breadcrumbs[breadcrumbs.length - 1],
}
}, [displayBreadcrumbNum, prefix])
}, [displayBreadcrumbNum, breadcrumbs])
const handleBackToBucketList = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix, setBucket } = dataSourceStore.getState()
const { setFileList, setSelectedFileIds, setBreadcrumbs, setPrefix, setBucket } = dataSourceStore.getState()
setFileList([])
setSelectedFileIds([])
setBucket('')
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleClickBucketName = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
const { setFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
setFileList([])
setSelectedFileIds([])
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleBackToRoot = useCallback(() => {
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
const { setFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
setFileList([])
setSelectedFileIds([])
setBreadcrumbs([])
setPrefix([])
}, [dataSourceStore])
const handleClickBreadcrumb = useCallback((index: number) => {
const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
const { breadcrumbs, prefix, setFileList, setSelectedFileIds, setBreadcrumbs, setPrefix } = dataSourceStore.getState()
const newBreadcrumbs = breadcrumbs.slice(0, index + 1)
const newPrefix = prefix.slice(0, index + 1)
setFileList([])
setSelectedFileIds([])
setBreadcrumbs(newBreadcrumbs)
setPrefix(newPrefix)
}, [dataSourceStore])
@ -80,7 +85,7 @@ const Breadcrumbs = ({
<div className='system-sm-medium text-test-secondary px-[5px]'>
{t('datasetPipeline.onlineDrive.breadcrumbs.searchResult', {
searchResultsLength,
folderName: prefix.length > 0 ? prefix[prefix.length - 1] : bucket,
folderName: breadcrumbs.length > 0 ? breadcrumbs[breadcrumbs.length - 1] : bucket,
})}
</div>
)}
@ -96,21 +101,21 @@ const Breadcrumbs = ({
bucketName={bucket}
handleBackToBucketList={handleBackToBucketList}
handleClickBucketName={handleClickBucketName}
isActive={prefix.length === 0}
disabled={prefix.length === 0}
showSeparator={prefix.length > 0}
isActive={breadcrumbs.length === 0}
disabled={breadcrumbs.length === 0}
showSeparator={breadcrumbs.length > 0}
/>
)}
{!hasBucket && (
<Drive
prefix={prefix}
breadcrumbs={breadcrumbs}
handleBackToRoot={handleBackToRoot}
/>
)}
{!breadcrumbs.needCollapsed && (
{!breadcrumbsConfig.needCollapsed && (
<>
{breadcrumbs.original.map((breadcrumb, index) => {
const isLast = index === breadcrumbs.original.length - 1
{breadcrumbsConfig.original.map((breadcrumb, index) => {
const isLast = index === breadcrumbsConfig.original.length - 1
return (
<BreadcrumbItem
key={`${breadcrumb}-${index}`}
@ -125,9 +130,9 @@ const Breadcrumbs = ({
})}
</>
)}
{breadcrumbs.needCollapsed && (
{breadcrumbsConfig.needCollapsed && (
<>
{breadcrumbs.prefixBreadcrumbs.map((breadcrumb, index) => {
{breadcrumbsConfig.prefixBreadcrumbs.map((breadcrumb, index) => {
return (
<BreadcrumbItem
key={`${breadcrumb}-${index}`}
@ -138,14 +143,14 @@ const Breadcrumbs = ({
)
})}
<Dropdown
startIndex={breadcrumbs.prefixBreadcrumbs.length}
breadcrumbs={breadcrumbs.collapsedBreadcrumbs}
startIndex={breadcrumbsConfig.prefixBreadcrumbs.length}
breadcrumbs={breadcrumbsConfig.collapsedBreadcrumbs}
onBreadcrumbClick={handleClickBreadcrumb}
/>
<BreadcrumbItem
index={prefix.length - 1}
index={breadcrumbs.length - 1}
handleClick={handleClickBreadcrumb}
name={breadcrumbs.lastBreadcrumb}
name={breadcrumbsConfig.lastBreadcrumb}
isActive={true}
disabled={true}
showSeparator={false}

View File

@ -4,7 +4,7 @@ import Input from '@/app/components/base/input'
import { useTranslation } from 'react-i18next'
type HeaderProps = {
prefix: string[]
breadcrumbs: string[]
inputValue: string
keywords: string
bucket: string
@ -15,7 +15,7 @@ type HeaderProps = {
}
const Header = ({
prefix,
breadcrumbs,
inputValue,
keywords,
bucket,
@ -29,7 +29,7 @@ const Header = ({
return (
<div className='flex items-center gap-x-2 bg-components-panel-bg p-1 pl-3'>
<Breadcrumbs
prefix={prefix}
breadcrumbs={breadcrumbs}
keywords={keywords}
bucket={bucket}
searchResultsLength={searchResultsLength}

View File

@ -7,7 +7,7 @@ import { useDebounceFn } from 'ahooks'
type FileListProps = {
fileList: OnlineDriveFile[]
selectedFileIds: string[]
prefix: string[]
breadcrumbs: string[]
keywords: string
bucket: string
isInPipeline: boolean
@ -22,7 +22,7 @@ type FileListProps = {
const FileList = ({
fileList,
selectedFileIds,
prefix,
breadcrumbs,
keywords,
bucket,
resetKeywords,
@ -56,7 +56,7 @@ const FileList = ({
return (
<div className='flex h-[400px] flex-col overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg shadow-xs shadow-shadow-shadow-3'>
<Header
prefix={prefix}
breadcrumbs={breadcrumbs}
inputValue={inputValue}
keywords={keywords}
bucket={bucket}

View File

@ -44,7 +44,7 @@ const List = ({
observerRef.current.observe(anchorRef.current)
}
return () => observerRef.current?.disconnect()
}, [anchorRef])
}, [anchorRef, isLoading])
const isAllLoading = isLoading && fileList.length === 0 && keywords.length === 0
const isPartialLoading = isLoading && fileList.length > 0

View File

@ -33,6 +33,7 @@ const OnlineDrive = ({
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
const {
nextPageParameters,
breadcrumbs,
prefix,
keywords,
bucket,
@ -41,6 +42,7 @@ const OnlineDrive = ({
currentCredentialId,
} = useDataSourceStoreWithSelector(useShallow(state => ({
nextPageParameters: state.nextPageParameters,
breadcrumbs: state.breadcrumbs,
prefix: state.prefix,
keywords: state.keywords,
bucket: state.bucket,
@ -62,17 +64,16 @@ const OnlineDrive = ({
const getOnlineDriveFiles = useCallback(async () => {
const { nextPageParameters, prefix, bucket, fileList, currentCredentialId } = dataSourceStore.getState()
const prefixString = prefix.length > 0 ? `${prefix.join('/')}/` : ''
setIsLoading(true)
ssePost(
datasourceNodeRunURL,
{
body: {
inputs: {
prefix: prefixString,
prefix: prefix[prefix.length - 1],
bucket,
next_page_parameters: nextPageParameters,
max_keys: 30, // Adjust as needed
max_keys: 30,
},
datasource_type: DatasourceType.onlineDrive,
credential_id: currentCredentialId,
@ -86,7 +87,7 @@ const OnlineDrive = ({
isTruncated: newIsTruncated,
nextPageParameters: newNextPageParameters,
hasBucket: newHasBucket,
} = convertOnlineDriveData(documentsData.data, prefix, bucket)
} = convertOnlineDriveData(documentsData.data, breadcrumbs, bucket)
setFileList([...fileList, ...newFileList])
isTruncated.current = newIsTruncated
currentNextPageParametersRef.current = newNextPageParameters
@ -105,6 +106,7 @@ const OnlineDrive = ({
}, [datasourceNodeRunURL, dataSourceStore])
useEffect(() => {
if (!currentCredentialId) return
if (isInitialMount) {
// Only fetch files on initial mount if fileList is empty
if (fileList.length === 0)
@ -150,7 +152,7 @@ const OnlineDrive = ({
}, [dataSourceStore, isInPipeline])
const handleOpenFolder = useCallback((file: OnlineDriveFile) => {
const { prefix, setPrefix, setBucket, setFileList, setSelectedFileIds } = dataSourceStore.getState()
const { breadcrumbs, setBreadcrumbs, setPrefix, setBucket, setFileList, setSelectedFileIds } = dataSourceStore.getState()
if (file.type === OnlineDriveFileType.file) return
setFileList([])
if (file.type === OnlineDriveFileType.bucket) {
@ -158,9 +160,13 @@ const OnlineDrive = ({
}
else {
setSelectedFileIds([])
const newPrefix = produce(prefix, (draft) => {
const newBreadcrumbs = produce(breadcrumbs, (draft) => {
draft.push(file.name)
})
const newPrefix = produce(prefix, (draft) => {
draft.push(file.id)
})
setBreadcrumbs(newBreadcrumbs)
setPrefix(newPrefix)
}
}, [dataSourceStore, getOnlineDriveFiles])
@ -185,7 +191,7 @@ const OnlineDrive = ({
<FileList
fileList={onlineDriveFileList}
selectedFileIds={selectedFileIds}
prefix={prefix}
breadcrumbs={breadcrumbs}
keywords={keywords}
bucket={bucket}
resetKeywords={resetKeywords}

View File

@ -2,6 +2,8 @@ import type { StateCreator } from 'zustand'
import type { OnlineDriveFile } from '@/models/pipeline'
export type OnlineDriveSliceShape = {
breadcrumbs: string[]
setBreadcrumbs: (breadcrumbs: string[]) => void
prefix: string[]
setPrefix: (prefix: string[]) => void
keywords: string
@ -23,6 +25,10 @@ export type OnlineDriveSliceShape = {
export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set, get) => {
return ({
breadcrumbs: [],
setBreadcrumbs: (breadcrumbs: string[]) => set(() => ({
breadcrumbs,
})),
prefix: [],
setPrefix: (prefix: string[]) => set(() => ({
prefix,