'use client' import type { GetAppsData } from '@dify/contracts/api/console/apps/types.gen' import type { AppListCreationDialog } from './app-list-creation-modals' import type { AppListUrlQuery } from './query-params' import type { App } from '@/models/explore' import type { TryAppSelection } from '@/types/try-app' import { ScrollArea, ScrollAreaContent, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, } from '@langgenius/dify-ui/scroll-area' import { useDebounce } from 'ahooks' import { useAtomValue } from 'jotai' import { useQueryStates } from 'nuqs' import { useCallback, useId, useMemo, useRef, useState } from 'react' import { workspacePermissionKeysAtom } from '@/context/permission-state' import { CheckModal } from '@/hooks/use-pay' import { hasPermission } from '@/utils/permission' import { AppListCatalog } from './app-list-catalog' import { AppListCreationModals } from './app-list-creation-modals' import { AppListHeader } from './app-list-header' import { AppListTagManagementModal } from './app-list-tag-management-modal' import { APP_LIST_SEARCH_DEBOUNCE_MS } from './constants' import { useDSLDragDrop } from './hooks/use-dsl-drag-drop' import { appListQueryParsers } from './query-params' type AppListQuery = NonNullable type AppListSortBy = NonNullable type AppListCategory = AppListUrlQuery['category'] type Props = Readonly<{ onCreateLearnDify?: (app: App) => void onTryLearnDify?: (params: TryAppSelection) => void }> export function List({ onCreateLearnDify, onTryLearnDify }: Props) { const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) const [urlQuery, setUrlQuery] = useQueryStates(appListQueryParsers) const { category, keywords } = urlQuery const [creatorIDs, setCreatorIDs] = useState([]) const [tagIDs, setTagIDs] = useState([]) const [sortBy, setSortBy] = useState('last_modified') const debouncedKeywords = useDebounce(keywords, { wait: APP_LIST_SEARCH_DEBOUNCE_MS }) const dropZoneRef = useRef(null) const scrollViewportRef = useRef(null) const titleId = useId() const [showTagManagementModal, setShowTagManagementModal] = useState(false) const [creationDialog, setCreationDialog] = useState(null) const canCreateApp = hasPermission(workspacePermissionKeys, 'app.create_and_management') const hasActiveFilters = category !== 'all' || tagIDs.length > 0 || keywords.trim().length > 0 || debouncedKeywords.trim().length > 0 || creatorIDs.length > 0 const handleDSLFileDropped = useCallback( (file: File) => { if (!canCreateApp) return setCreationDialog({ type: 'dsl', droppedFile: file }) }, [canCreateApp], ) const { dragging } = useDSLDragDrop({ onDSLFileDropped: handleDSLFileDropped, dropZoneRef, enabled: canCreateApp, }) const appListQuery = useMemo( () => ({ page: 1, limit: 30, name: debouncedKeywords, sort_by: sortBy, ...(tagIDs.length ? { tag_ids: tagIDs } : {}), ...(creatorIDs.length ? { creator_ids: creatorIDs } : {}), ...(category !== 'all' ? { mode: category } : {}), }), [category, creatorIDs, debouncedKeywords, sortBy, tagIDs], ) const resetCatalogScroll = () => { scrollViewportRef.current?.scrollTo({ top: 0 }) } const changeCategory = (nextCategory: AppListCategory) => { resetCatalogScroll() void setUrlQuery({ category: nextCategory }) } const changeTagIDs = (nextTagIDs: string[]) => { resetCatalogScroll() setTagIDs(nextTagIDs) } const changeKeywords = (nextKeywords: string) => { resetCatalogScroll() void setUrlQuery({ keywords: nextKeywords }) } const changeCreatorIDs = (nextCreatorIDs: string[]) => { resetCatalogScroll() setCreatorIDs(nextCreatorIDs) } const changeSortBy = (nextSortBy: AppListSortBy) => { resetCatalogScroll() setSortBy(nextSortBy) } const openCreateBlankModal = useCallback(() => { if (canCreateApp) setCreationDialog({ type: 'blank' }) }, [canCreateApp]) const openCreateTemplateDialog = useCallback(() => { if (canCreateApp) setCreationDialog({ type: 'template' }) }, [canCreateApp]) const openCreateFromDSLModal = useCallback(() => { if (canCreateApp) setCreationDialog({ type: 'dsl' }) }, [canCreateApp]) const openTagManagement = useCallback(() => setShowTagManagementModal(true), []) return ( <>
{dragging && (
)}
setShowTagManagementModal(false)} />
setCreationDialog(null)} onOpenBlank={openCreateBlankModal} onOpenTemplate={openCreateTemplateDialog} /> ) }