mirror of https://github.com/langgenius/dify.git
feat: surface featured trigger recommendations in start tab (#27319)
This commit is contained in:
parent
9453148233
commit
b94ad084c3
|
|
@ -2,11 +2,13 @@
|
|||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { BlockEnum, OnSelectBlock } from '../types'
|
||||
import type { TriggerDefaultValue } from './types'
|
||||
import type { TriggerDefaultValue, TriggerWithProvider } from './types'
|
||||
import StartBlocks from './start-blocks'
|
||||
import TriggerPluginList from './trigger-plugin/list'
|
||||
import { ENTRY_NODE_TYPES } from './constants'
|
||||
|
|
@ -17,6 +19,11 @@ import { getMarketplaceUrl } from '@/utils/var'
|
|||
import Button from '@/app/components/base/button'
|
||||
import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { BlockEnum as BlockEnumValue } from '../types'
|
||||
import FeaturedTriggers from './featured-triggers'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers'
|
||||
import { useFeaturedTriggersRecommendations } from '@/service/use-plugins'
|
||||
|
||||
const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'
|
||||
|
||||
|
|
@ -38,11 +45,39 @@ const AllStartBlocks = ({
|
|||
const { t } = useTranslation()
|
||||
const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false)
|
||||
const [hasPluginContent, setHasPluginContent] = useState(false)
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
|
||||
const entryNodeTypes = availableBlocksTypes?.length
|
||||
? availableBlocksTypes
|
||||
: ENTRY_NODE_TYPES
|
||||
const enableTriggerPlugin = entryNodeTypes.includes(BlockEnumValue.TriggerPlugin)
|
||||
const { data: triggerProviders = [] } = useAllTriggerPlugins(enableTriggerPlugin)
|
||||
const providerMap = useMemo(() => {
|
||||
const map = new Map<string, TriggerWithProvider>()
|
||||
triggerProviders.forEach((provider) => {
|
||||
const keys = [
|
||||
provider.plugin_id,
|
||||
provider.plugin_unique_identifier,
|
||||
provider.id,
|
||||
].filter(Boolean) as string[]
|
||||
keys.forEach((key) => {
|
||||
if (!map.has(key))
|
||||
map.set(key, provider)
|
||||
})
|
||||
})
|
||||
return map
|
||||
}, [triggerProviders])
|
||||
const invalidateTriggers = useInvalidateAllTriggerPlugins()
|
||||
const trimmedSearchText = searchText.trim()
|
||||
const hasSearchText = trimmedSearchText.length > 0
|
||||
const {
|
||||
plugins: featuredPlugins = [],
|
||||
isLoading: featuredLoading,
|
||||
} = useFeaturedTriggersRecommendations(enableTriggerPlugin && enable_marketplace && !hasSearchText)
|
||||
|
||||
const shouldShowFeatured = enableTriggerPlugin
|
||||
&& enable_marketplace
|
||||
&& !hasSearchText
|
||||
|
||||
const handleStartBlocksContentChange = useCallback((hasContent: boolean) => {
|
||||
setHasStartBlocksContent(hasContent)
|
||||
|
|
@ -52,8 +87,8 @@ const AllStartBlocks = ({
|
|||
setHasPluginContent(hasContent)
|
||||
}, [])
|
||||
|
||||
const hasAnyContent = hasStartBlocksContent || hasPluginContent
|
||||
const shouldShowEmptyState = searchText && !hasAnyContent
|
||||
const hasAnyContent = hasStartBlocksContent || hasPluginContent || shouldShowFeatured
|
||||
const shouldShowEmptyState = hasSearchText && !hasAnyContent
|
||||
|
||||
useEffect(() => {
|
||||
if (!enableTriggerPlugin && hasPluginContent)
|
||||
|
|
@ -65,8 +100,27 @@ const AllStartBlocks = ({
|
|||
<div className='flex max-h-[640px] flex-col'>
|
||||
<div className='flex-1 overflow-y-auto'>
|
||||
<div className={cn(shouldShowEmptyState && 'hidden')}>
|
||||
{shouldShowFeatured && (
|
||||
<>
|
||||
<FeaturedTriggers
|
||||
plugins={featuredPlugins}
|
||||
providerMap={providerMap}
|
||||
onSelect={onSelect}
|
||||
isLoading={featuredLoading}
|
||||
onInstallSuccess={async () => {
|
||||
invalidateTriggers()
|
||||
}}
|
||||
/>
|
||||
<div className='px-3'>
|
||||
<Divider className='!h-px' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className='px-3 pb-1 pt-2'>
|
||||
<span className='system-xs-medium text-text-primary'>{t('workflow.tabs.allTriggers')}</span>
|
||||
</div>
|
||||
<StartBlocks
|
||||
searchText={searchText}
|
||||
searchText={trimmedSearchText}
|
||||
onSelect={onSelect as OnSelectBlock}
|
||||
availableBlocksTypes={entryNodeTypes as unknown as BlockEnum[]}
|
||||
onContentStateChange={handleStartBlocksContentChange}
|
||||
|
|
@ -75,7 +129,7 @@ const AllStartBlocks = ({
|
|||
{enableTriggerPlugin && (
|
||||
<TriggerPluginList
|
||||
onSelect={onSelect}
|
||||
searchText={searchText}
|
||||
searchText={trimmedSearchText}
|
||||
onContentStateChange={handlePluginContentChange}
|
||||
tags={tags}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,326 @@
|
|||
'use client'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BlockEnum } from '../types'
|
||||
import type { TriggerDefaultValue, TriggerWithProvider } from './types'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import BlockIcon from '../block-icon'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { RiMoreLine } from '@remixicon/react'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Link from 'next/link'
|
||||
import { getMarketplaceUrl } from '@/utils/var'
|
||||
import TriggerPluginItem from './trigger-plugin/item'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import Action from '@/app/components/workflow/block-selector/market-place-plugin/action'
|
||||
import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
|
||||
const MAX_RECOMMENDED_COUNT = 15
|
||||
const INITIAL_VISIBLE_COUNT = 5
|
||||
|
||||
type FeaturedTriggersProps = {
|
||||
plugins: Plugin[]
|
||||
providerMap: Map<string, TriggerWithProvider>
|
||||
onSelect: (type: BlockEnum, trigger?: TriggerDefaultValue) => void
|
||||
isLoading?: boolean
|
||||
onInstallSuccess?: () => void | Promise<void>
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'workflow_triggers_featured_collapsed'
|
||||
|
||||
const FeaturedTriggers = ({
|
||||
plugins,
|
||||
providerMap,
|
||||
onSelect,
|
||||
isLoading = false,
|
||||
onInstallSuccess,
|
||||
}: FeaturedTriggersProps) => {
|
||||
const { t } = useTranslation()
|
||||
const language = useGetLanguage()
|
||||
const [visibleCount, setVisibleCount] = useState(INITIAL_VISIBLE_COUNT)
|
||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(() => {
|
||||
if (typeof window === 'undefined')
|
||||
return false
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY)
|
||||
return stored === 'true'
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined')
|
||||
return
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY)
|
||||
if (stored !== null)
|
||||
setIsCollapsed(stored === 'true')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined')
|
||||
return
|
||||
window.localStorage.setItem(STORAGE_KEY, String(isCollapsed))
|
||||
}, [isCollapsed])
|
||||
|
||||
useEffect(() => {
|
||||
setVisibleCount(INITIAL_VISIBLE_COUNT)
|
||||
}, [plugins])
|
||||
|
||||
const limitedPlugins = useMemo(
|
||||
() => plugins.slice(0, MAX_RECOMMENDED_COUNT),
|
||||
[plugins],
|
||||
)
|
||||
|
||||
const {
|
||||
installedProviders,
|
||||
uninstalledPlugins,
|
||||
} = useMemo(() => {
|
||||
const installed: TriggerWithProvider[] = []
|
||||
const uninstalled: Plugin[] = []
|
||||
const visitedProviderIds = new Set<string>()
|
||||
|
||||
limitedPlugins.forEach((plugin) => {
|
||||
const provider = providerMap.get(plugin.plugin_id) || providerMap.get(plugin.latest_package_identifier)
|
||||
if (provider) {
|
||||
if (!visitedProviderIds.has(provider.id)) {
|
||||
installed.push(provider)
|
||||
visitedProviderIds.add(provider.id)
|
||||
}
|
||||
}
|
||||
else {
|
||||
uninstalled.push(plugin)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
installedProviders: installed,
|
||||
uninstalledPlugins: uninstalled,
|
||||
}
|
||||
}, [limitedPlugins, providerMap])
|
||||
|
||||
const totalQuota = Math.min(visibleCount, MAX_RECOMMENDED_COUNT)
|
||||
|
||||
const visibleInstalledProviders = useMemo(
|
||||
() => installedProviders.slice(0, totalQuota),
|
||||
[installedProviders, totalQuota],
|
||||
)
|
||||
|
||||
const remainingSlots = Math.max(totalQuota - visibleInstalledProviders.length, 0)
|
||||
|
||||
const visibleUninstalledPlugins = useMemo(
|
||||
() => (remainingSlots > 0 ? uninstalledPlugins.slice(0, remainingSlots) : []),
|
||||
[uninstalledPlugins, remainingSlots],
|
||||
)
|
||||
|
||||
const totalVisible = visibleInstalledProviders.length + visibleUninstalledPlugins.length
|
||||
const maxAvailable = Math.min(MAX_RECOMMENDED_COUNT, installedProviders.length + uninstalledPlugins.length)
|
||||
const hasMoreToShow = totalVisible < maxAvailable
|
||||
const canToggleVisibility = maxAvailable > INITIAL_VISIBLE_COUNT
|
||||
const isExpanded = canToggleVisibility && !hasMoreToShow
|
||||
const showEmptyState = !isLoading && totalVisible === 0
|
||||
|
||||
return (
|
||||
<div className='px-3 pb-3 pt-2'>
|
||||
<button
|
||||
type='button'
|
||||
className='flex w-full items-center rounded-md px-0 py-1 text-left text-text-primary'
|
||||
onClick={() => setIsCollapsed(prev => !prev)}
|
||||
>
|
||||
<span className='system-xs-medium text-text-primary'>{t('workflow.tabs.featuredTools')}</span>
|
||||
<ArrowDownRoundFill className={`ml-0.5 h-4 w-4 text-text-tertiary transition-transform ${isCollapsed ? '-rotate-90' : 'rotate-0'}`} />
|
||||
</button>
|
||||
|
||||
{!isCollapsed && (
|
||||
<>
|
||||
{isLoading && (
|
||||
<div className='py-3'>
|
||||
<Loading type='app' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showEmptyState && (
|
||||
<p className='system-xs-regular py-2 text-text-tertiary'>
|
||||
<Link className='text-text-accent' href={getMarketplaceUrl('', { category: 'trigger' })} target='_blank' rel='noopener noreferrer'>
|
||||
{t('workflow.tabs.noFeaturedTriggers')}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!showEmptyState && !isLoading && (
|
||||
<>
|
||||
{visibleInstalledProviders.length > 0 && (
|
||||
<div className='mt-1'>
|
||||
{visibleInstalledProviders.map(provider => (
|
||||
<TriggerPluginItem
|
||||
key={provider.id}
|
||||
payload={provider}
|
||||
hasSearchText={false}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{visibleUninstalledPlugins.length > 0 && (
|
||||
<div className='mt-1 flex flex-col gap-1'>
|
||||
{visibleUninstalledPlugins.map(plugin => (
|
||||
<FeaturedTriggerUninstalledItem
|
||||
key={plugin.plugin_id}
|
||||
plugin={plugin}
|
||||
language={language}
|
||||
onInstallSuccess={async () => {
|
||||
await onInstallSuccess?.()
|
||||
}}
|
||||
t={t}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && totalVisible > 0 && canToggleVisibility && (
|
||||
<div
|
||||
className='group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary'
|
||||
onClick={() => {
|
||||
setVisibleCount((count) => {
|
||||
if (count >= maxAvailable)
|
||||
return INITIAL_VISIBLE_COUNT
|
||||
|
||||
return Math.min(count + INITIAL_VISIBLE_COUNT, maxAvailable)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary'>
|
||||
<RiMoreLine className='size-4 group-hover:hidden' />
|
||||
{isExpanded ? (
|
||||
<ArrowUpDoubleLine className='hidden size-4 group-hover:block' />
|
||||
) : (
|
||||
<ArrowDownDoubleLine className='hidden size-4 group-hover:block' />
|
||||
)}
|
||||
</div>
|
||||
<div className='system-xs-regular'>
|
||||
{t(isExpanded ? 'workflow.tabs.showLessFeatured' : 'workflow.tabs.showMoreFeatured')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type FeaturedTriggerUninstalledItemProps = {
|
||||
plugin: Plugin
|
||||
language: string
|
||||
onInstallSuccess?: () => Promise<void> | void
|
||||
t: (key: string, options?: Record<string, any>) => string
|
||||
}
|
||||
|
||||
function FeaturedTriggerUninstalledItem({
|
||||
plugin,
|
||||
language,
|
||||
onInstallSuccess,
|
||||
t,
|
||||
}: FeaturedTriggerUninstalledItemProps) {
|
||||
const label = plugin.label?.[language] || plugin.name
|
||||
const description = typeof plugin.brief === 'object' ? plugin.brief[language] : plugin.brief
|
||||
const installCountLabel = t('plugin.install', { num: formatNumber(plugin.install_count || 0) })
|
||||
const [actionOpen, setActionOpen] = useState(false)
|
||||
const [isActionHovered, setIsActionHovered] = useState(false)
|
||||
const [isInstallModalOpen, setIsInstallModalOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!actionOpen)
|
||||
return
|
||||
|
||||
const handleScroll = () => {
|
||||
setActionOpen(false)
|
||||
setIsActionHovered(false)
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll, true)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll, true)
|
||||
}
|
||||
}, [actionOpen])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
position='right'
|
||||
needsDelay={false}
|
||||
popupClassName='!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg'
|
||||
popupContent={(
|
||||
<div>
|
||||
<BlockIcon size='md' className='mb-2' type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} />
|
||||
<div className='mb-1 text-sm leading-5 text-text-primary'>{label}</div>
|
||||
<div className='text-xs leading-[18px] text-text-secondary'>{description}</div>
|
||||
</div>
|
||||
)}
|
||||
disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
|
||||
>
|
||||
<div
|
||||
className='group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover'
|
||||
>
|
||||
<div className='flex h-full min-w-0 items-center'>
|
||||
<BlockIcon type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} />
|
||||
<div className='ml-2 min-w-0'>
|
||||
<div className='system-sm-medium truncate text-text-secondary'>{label}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ml-auto flex h-full items-center gap-1 pl-1'>
|
||||
<span className={`system-xs-regular text-text-tertiary ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span>
|
||||
<div
|
||||
className={`system-xs-medium flex h-full items-center gap-1 text-components-button-secondary-accent-text [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? 'flex' : 'hidden group-hover:flex'}`}
|
||||
onMouseEnter={() => setIsActionHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
if (!actionOpen)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type='button'
|
||||
className='cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover'
|
||||
onClick={() => {
|
||||
setActionOpen(false)
|
||||
setIsInstallModalOpen(true)
|
||||
setIsActionHovered(true)
|
||||
}}
|
||||
>
|
||||
{t('plugin.installAction')}
|
||||
</button>
|
||||
<Action
|
||||
open={actionOpen}
|
||||
onOpenChange={(value) => {
|
||||
setActionOpen(value)
|
||||
setIsActionHovered(value)
|
||||
}}
|
||||
author={plugin.org}
|
||||
name={plugin.name}
|
||||
version={plugin.latest_version}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{isInstallModalOpen && (
|
||||
<InstallFromMarketplace
|
||||
uniqueIdentifier={plugin.latest_package_identifier}
|
||||
manifest={plugin}
|
||||
onSuccess={async () => {
|
||||
setIsInstallModalOpen(false)
|
||||
setIsActionHovered(false)
|
||||
await onInstallSuccess?.()
|
||||
}}
|
||||
onClose={() => {
|
||||
setIsInstallModalOpen(false)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturedTriggers
|
||||
|
|
@ -260,6 +260,7 @@ const translation = {
|
|||
'blocks': 'Nodes',
|
||||
'searchTool': 'Search tool',
|
||||
'searchTrigger': 'Search triggers...',
|
||||
'allTriggers': 'All triggers',
|
||||
'tools': 'Tools',
|
||||
'allTool': 'All',
|
||||
'plugin': 'Plugin',
|
||||
|
|
@ -285,6 +286,7 @@ const translation = {
|
|||
'usePlugin': 'Select tool',
|
||||
'hideActions': 'Hide tools',
|
||||
'noFeaturedPlugins': 'Discover more tools in Marketplace',
|
||||
'noFeaturedTriggers': 'Discover more triggers in Marketplace',
|
||||
},
|
||||
blocks: {
|
||||
'start': 'User Input',
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ const translation = {
|
|||
'searchTool': 'ツール検索',
|
||||
'searchTrigger': 'トリガー検索...',
|
||||
'tools': 'ツール',
|
||||
'allTriggers': 'すべてのトリガー',
|
||||
'allTool': 'すべて',
|
||||
'customTool': 'カスタム',
|
||||
'workflowTool': 'ワークフロー',
|
||||
|
|
@ -255,6 +256,8 @@ const translation = {
|
|||
'requestToCommunity': 'コミュニティにリクエスト',
|
||||
'plugin': 'プラグイン',
|
||||
'agent': 'エージェント戦略',
|
||||
'noFeaturedPlugins': 'マーケットプレイスでさらにツールを見つける',
|
||||
'noFeaturedTriggers': 'マーケットプレイスでさらにトリガーを見つける',
|
||||
'addAll': 'すべてを追加する',
|
||||
'allAdded': 'すべて追加されました',
|
||||
'searchDataSource': 'データソースを検索',
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ const translation = {
|
|||
'blocks': '节点',
|
||||
'searchTool': '搜索工具',
|
||||
'searchTrigger': '搜索触发器...',
|
||||
'allTriggers': '全部触发器',
|
||||
'tools': '工具',
|
||||
'allTool': '全部',
|
||||
'plugin': '插件',
|
||||
|
|
@ -271,6 +272,7 @@ const translation = {
|
|||
'usePlugin': '选择工具',
|
||||
'hideActions': '收起工具',
|
||||
'noFeaturedPlugins': '前往插件市场查看更多工具',
|
||||
'noFeaturedTriggers': '前往插件市场查看更多触发器',
|
||||
},
|
||||
blocks: {
|
||||
'start': '用户输入',
|
||||
|
|
|
|||
|
|
@ -222,6 +222,8 @@ const translation = {
|
|||
'searchBlock': '搜索節點',
|
||||
'blocks': '節點',
|
||||
'tools': '工具',
|
||||
'searchTrigger': '搜尋觸發器...',
|
||||
'allTriggers': '所有觸發器',
|
||||
'allTool': '全部',
|
||||
'customTool': '自定義',
|
||||
'workflowTool': '工作流',
|
||||
|
|
@ -237,6 +239,8 @@ const translation = {
|
|||
'addAll': '全部添加',
|
||||
'sources': '來源',
|
||||
'searchDataSource': '搜尋資料來源',
|
||||
'noFeaturedPlugins': '前往 Marketplace 查看更多工具',
|
||||
'noFeaturedTriggers': '前往 Marketplace 查看更多觸發器',
|
||||
},
|
||||
blocks: {
|
||||
'start': '開始',
|
||||
|
|
|
|||
|
|
@ -69,22 +69,21 @@ export const useCheckInstalled = ({
|
|||
|
||||
const useRecommendedMarketplacePluginsKey = [NAME_SPACE, 'recommendedMarketplacePlugins']
|
||||
export const useRecommendedMarketplacePlugins = ({
|
||||
category = PluginCategoryEnum.tool,
|
||||
collection = '__recommended-plugins-tools',
|
||||
enabled = true,
|
||||
limit = 15,
|
||||
}: {
|
||||
category?: string
|
||||
collection?: string
|
||||
enabled?: boolean
|
||||
limit?: number
|
||||
} = {}) => {
|
||||
return useQuery<Plugin[]>({
|
||||
queryKey: [...useRecommendedMarketplacePluginsKey, category, limit],
|
||||
queryKey: [...useRecommendedMarketplacePluginsKey, collection, limit],
|
||||
queryFn: async () => {
|
||||
const response = await postMarketplace<{ data: { plugins: Plugin[] } }>(
|
||||
'/collections/__recommended-plugins-overall/plugins',
|
||||
`/collections/${collection}/plugins`,
|
||||
{
|
||||
body: {
|
||||
category,
|
||||
limit,
|
||||
},
|
||||
},
|
||||
|
|
@ -101,6 +100,23 @@ export const useFeaturedToolsRecommendations = (enabled: boolean, limit = 15) =>
|
|||
data: plugins = [],
|
||||
isLoading,
|
||||
} = useRecommendedMarketplacePlugins({
|
||||
collection: '__recommended-plugins-tools',
|
||||
enabled,
|
||||
limit,
|
||||
})
|
||||
|
||||
return {
|
||||
plugins,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useFeaturedTriggersRecommendations = (enabled: boolean, limit = 15) => {
|
||||
const {
|
||||
data: plugins = [],
|
||||
isLoading,
|
||||
} = useRecommendedMarketplacePlugins({
|
||||
collection: '__recommended-plugins-triggers',
|
||||
enabled,
|
||||
limit,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue