'use client'
import type { SourceAppPickerValue } from '../state'
import type { App } from '@/types/app'
import { cn } from '@langgenius/dify-ui/cn'
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxInputGroup,
ComboboxItem,
ComboboxItemText,
ComboboxList,
ComboboxTrigger,
} from '@langgenius/dify-ui/combobox'
import { useAtomValue, useSetAtom } from 'jotai'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import { SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
import { useInfiniteScroll } from '@/features/deployments/shared/hooks/use-infinite-scroll'
import { TitleTooltip } from '../../shared/components/title-tooltip'
import {
createReleaseSourceAppSearchTextAtom,
createReleaseSourceAppsQueryAtom,
} from '../state'
const SOURCE_APP_PICKER_SKELETON_KEYS = ['first-source-app', 'second-source-app', 'third-source-app']
function sourceAppSearchText(app: App) {
return `${app.name} ${app.id}`.toLowerCase()
}
function SourceAppTrigger({ app }: {
app?: SourceAppPickerValue
}) {
const { t } = useTranslation('deployments')
return (
{app && (
)}
{app?.name ?? t('createModal.appPickerPlaceholder')}
)
}
function SourceAppOption({ app }: {
app: App
}) {
return (
{app.name}
(
{app.id.slice(0, 8)}
)
)
}
function SourceAppPickerSkeleton() {
return (
{SOURCE_APP_PICKER_SKELETON_KEYS.map(key => (
))}
)
}
export function SourceAppPicker({ value, onChange, disabled = false }: {
value?: SourceAppPickerValue
onChange: (app: App) => void
disabled?: boolean
}) {
const { t } = useTranslation('deployments')
const [isShow, setIsShow] = useState(false)
const searchText = useAtomValue(createReleaseSourceAppSearchTextAtom)
const setSearchText = useSetAtom(createReleaseSourceAppSearchTextAtom)
const sourceAppsQuery = useAtomValue(createReleaseSourceAppsQueryAtom)
const {
data,
isLoading,
isFetchingNextPage,
hasNextPage,
} = sourceAppsQuery
const { rootRef, sentinelRef } = useInfiniteScroll(sourceAppsQuery, {
enabled: isShow && !disabled,
rootMargin: '0px 0px 160px 0px',
threshold: 0.1,
})
const apps = data?.pages.flatMap(page => page.data) ?? []
return (
items={apps}
open={!disabled && isShow}
inputValue={searchText}
onOpenChange={(open) => {
setIsShow(disabled ? false : open)
}}
onInputValueChange={(value) => {
if (!disabled)
setSearchText(value)
}}
onValueChange={(app) => {
if (disabled)
return
if (!app)
return
onChange(app)
setIsShow(false)
}}
itemToStringLabel={(app) => {
if (!app)
return ''
return app.name
}}
itemToStringValue={(app) => {
if (!app)
return ''
return app.id
}}
filter={(app, query) => sourceAppSearchText(app).includes(query.toLowerCase())}
disabled={disabled}
>
{(isLoading || isFetchingNextPage) && apps.length === 0 &&
}
{(app: App) => (
)}
{!(isLoading || isFetchingNextPage) && (
{t('createModal.appSearchEmpty')}
)}
{isFetchingNextPage && apps.length > 0 && (
{t('createModal.loadingApps')}
)}
{hasNextPage &&
}
)
}