mirror of https://github.com/langgenius/dify.git
feat: Enhance Online Drive component with bucket handling and breadcrumb navigation
This commit is contained in:
parent
71d8a0c0b6
commit
cf46fba753
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type DriveProps = {
|
||||
prefix: string[]
|
||||
handleBackToRoot: () => void
|
||||
}
|
||||
|
||||
const Drive = ({
|
||||
prefix,
|
||||
handleBackToRoot,
|
||||
}: DriveProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type='button'
|
||||
className={cn(
|
||||
'max-w-full shrink truncate rounded-md px-[5px] py-1',
|
||||
prefix.length > 0 && 'system-sm-medium text-text-secondary hover:bg-state-base-hover',
|
||||
prefix.length === 0 && 'system-sm-regular text-text-tertiary',
|
||||
)}
|
||||
onClick={handleBackToRoot}
|
||||
disabled={prefix.length === 0}
|
||||
>
|
||||
{t('datasetPipeline.onlineDrive.breadcrumbs.allFiles')}
|
||||
</button>
|
||||
{prefix.length > 0 && <span className='system-xs-regular text-divider-deep'>/</span>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Drive)
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDataSourceStore } from '../../../../store'
|
||||
import { useDataSourceStore, useDataSourceStoreWithSelector } from '../../../../store'
|
||||
import Bucket from './bucket'
|
||||
import BreadcrumbItem from './item'
|
||||
import Dropdown from './dropdown'
|
||||
import Drive from './drive'
|
||||
|
||||
type BreadcrumbsProps = {
|
||||
prefix: string[]
|
||||
|
|
@ -22,8 +23,9 @@ const Breadcrumbs = ({
|
|||
}: BreadcrumbsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const dataSourceStore = useDataSourceStore()
|
||||
const hasBucket = useDataSourceStoreWithSelector(s => s.hasBucket)
|
||||
const showSearchResult = !!keywords && searchResultsLength > 0
|
||||
const isRoot = prefix.length === 0 && bucket === ''
|
||||
const showBucketListTitle = prefix.length === 0 && hasBucket && bucket === ''
|
||||
|
||||
const displayBreadcrumbNum = useMemo(() => {
|
||||
const num = isInPipeline ? 2 : 3
|
||||
|
|
@ -57,6 +59,13 @@ const Breadcrumbs = ({
|
|||
setPrefix([])
|
||||
}, [dataSourceStore])
|
||||
|
||||
const handleBackToRoot = useCallback(() => {
|
||||
const { setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
|
||||
setFileList([])
|
||||
setSelectedFileIds([])
|
||||
setPrefix([])
|
||||
}, [dataSourceStore])
|
||||
|
||||
const handleClickBreadcrumb = useCallback((index: number) => {
|
||||
const { prefix, setFileList, setSelectedFileIds, setPrefix } = dataSourceStore.getState()
|
||||
const newPrefix = prefix.slice(0, index + 1)
|
||||
|
|
@ -75,14 +84,14 @@ const Breadcrumbs = ({
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
{!showSearchResult && isRoot && (
|
||||
{!showSearchResult && showBucketListTitle && (
|
||||
<div className='system-sm-medium text-test-secondary px-[5px]'>
|
||||
{t('datasetPipeline.onlineDrive.breadcrumbs.allBuckets')}
|
||||
</div>
|
||||
)}
|
||||
{!showSearchResult && !isRoot && (
|
||||
{!showSearchResult && !showBucketListTitle && (
|
||||
<div className='flex w-full items-center gap-x-0.5 overflow-hidden'>
|
||||
{bucket && (
|
||||
{hasBucket && bucket && (
|
||||
<Bucket
|
||||
bucketName={bucket}
|
||||
handleBackToBucketList={handleBackToBucketList}
|
||||
|
|
@ -92,6 +101,12 @@ const Breadcrumbs = ({
|
|||
showSeparator={prefix.length > 0}
|
||||
/>
|
||||
)}
|
||||
{!hasBucket && (
|
||||
<Drive
|
||||
prefix={prefix}
|
||||
handleBackToRoot={handleBackToRoot}
|
||||
/>
|
||||
)}
|
||||
{!breadcrumbs.needCollapsed && (
|
||||
<>
|
||||
{breadcrumbs.original.map((breadcrumb, index) => {
|
||||
|
|
|
|||
|
|
@ -80,15 +80,17 @@ const OnlineDrive = ({
|
|||
},
|
||||
{
|
||||
onDataSourceNodeCompleted: (documentsData: DataSourceNodeCompletedResponse) => {
|
||||
const { setFileList, isTruncated, currentNextPageParametersRef } = dataSourceStore.getState()
|
||||
const { setFileList, isTruncated, currentNextPageParametersRef, setHasBucket } = dataSourceStore.getState()
|
||||
const {
|
||||
fileList: newFileList,
|
||||
isTruncated: newIsTruncated,
|
||||
nextPageParameters: newNextPageParameters,
|
||||
hasBucket: newHasBucket,
|
||||
} = convertOnlineDriveData(documentsData.data, prefix, bucket)
|
||||
setFileList([...fileList, ...newFileList])
|
||||
isTruncated.current = newIsTruncated
|
||||
currentNextPageParametersRef.current = newNextPageParameters
|
||||
setHasBucket(newHasBucket)
|
||||
setIsLoading(false)
|
||||
},
|
||||
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
|
||||
|
|
|
|||
|
|
@ -7,20 +7,23 @@ export const isFile = (type: 'file' | 'folder'): boolean => {
|
|||
|
||||
export const isBucketListInitiation = (data: OnlineDriveData[], prefix: string[], bucket: string): boolean => {
|
||||
if (bucket || prefix.length > 0) return false
|
||||
return data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0)
|
||||
const hasBucket = data.every(item => !!item.bucket)
|
||||
return hasBucket && (data.length > 1 || (data.length === 1 && !!data[0].bucket && data[0].files.length === 0))
|
||||
}
|
||||
|
||||
export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[], bucket: string): {
|
||||
fileList: OnlineDriveFile[],
|
||||
isTruncated: boolean,
|
||||
nextPageParameters: Record<string, any>
|
||||
hasBucket: boolean
|
||||
} => {
|
||||
const fileList: OnlineDriveFile[] = []
|
||||
let isTruncated = false
|
||||
let nextPageParameters: Record<string, any> = {}
|
||||
let hasBucket = false
|
||||
|
||||
if (data.length === 0)
|
||||
return { fileList, isTruncated, nextPageParameters }
|
||||
return { fileList, isTruncated, nextPageParameters, hasBucket }
|
||||
|
||||
if (isBucketListInitiation(data, prefix, bucket)) {
|
||||
data.forEach((item) => {
|
||||
|
|
@ -30,6 +33,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[]
|
|||
type: OnlineDriveFileType.bucket,
|
||||
})
|
||||
})
|
||||
hasBucket = true
|
||||
}
|
||||
else {
|
||||
data[0].files.forEach((file) => {
|
||||
|
|
@ -44,6 +48,7 @@ export const convertOnlineDriveData = (data: OnlineDriveData[], prefix: string[]
|
|||
})
|
||||
isTruncated = data[0].is_truncated ?? false
|
||||
nextPageParameters = data[0].next_page_parameters ?? {}
|
||||
hasBucket = !!data[0].bucket
|
||||
}
|
||||
return { fileList, isTruncated, nextPageParameters }
|
||||
return { fileList, isTruncated, nextPageParameters, hasBucket }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ export type OnlineDriveSliceShape = {
|
|||
setNextPageParameters: (nextPageParameters: Record<string, any>) => void
|
||||
isTruncated: React.RefObject<boolean>
|
||||
previewOnlineDriveFileRef: React.RefObject<OnlineDriveFile | undefined>
|
||||
hasBucket: boolean
|
||||
setHasBucket: (hasBucket: boolean) => void
|
||||
}
|
||||
|
||||
export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set, get) => {
|
||||
|
|
@ -53,5 +55,9 @@ export const createOnlineDriveSlice: StateCreator<OnlineDriveSliceShape> = (set,
|
|||
})),
|
||||
isTruncated: { current: false },
|
||||
previewOnlineDriveFileRef: { current: undefined },
|
||||
hasBucket: false,
|
||||
setHasBucket: (hasBucket: boolean) => set(() => ({
|
||||
hasBucket,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ const translation = {
|
|||
notConnectedTip: 'To sync with {{name}}, connection to {{name}} must be established first.',
|
||||
breadcrumbs: {
|
||||
allBuckets: 'All Cloud Storage Buckets',
|
||||
allFiles: 'All Files',
|
||||
searchResult: 'Find {{searchResultsLength}} items in "{{folderName}}" folder',
|
||||
searchPlaceholder: 'Search files...',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ const translation = {
|
|||
notConnectedTip: '同步 {{name}} 内容前, 须先绑定 {{name}}。',
|
||||
breadcrumbs: {
|
||||
allBuckets: '所有云存储桶',
|
||||
allFiles: '所有文件',
|
||||
searchResult: '在 "{{folderName}}" 文件夹中找到 {{searchResultsLength}} 个项目',
|
||||
searchPlaceholder: '搜索文件...',
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue