Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins

This commit is contained in:
Yi 2024-11-05 17:53:03 +08:00
commit 61a70e7a71
11 changed files with 134 additions and 71 deletions

View File

@ -123,7 +123,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
const [collapse, setCollapse] = useState(false)
const {
plugins,
plugins = [],
queryPlugins,
queryPluginsWithDebounced,
isLoading: isPluginsLoading,

View File

@ -1,72 +1,87 @@
import { useTranslation } from 'react-i18next'
type Tag = {
name: string
label: string
}
export const useTags = () => {
const { t } = useTranslation()
return [
const tags = [
{
name: 'search',
label: t('pluginTags.search'),
label: t('pluginTags.tags.search'),
},
{
name: 'image',
label: t('pluginTags.image'),
label: t('pluginTags.tags.image'),
},
{
name: 'videos',
label: t('pluginTags.videos'),
label: t('pluginTags.tags.videos'),
},
{
name: 'weather',
label: t('pluginTags.weather'),
label: t('pluginTags.tags.weather'),
},
{
name: 'finance',
label: t('pluginTags.finance'),
label: t('pluginTags.tags.finance'),
},
{
name: 'design',
label: t('pluginTags.design'),
label: t('pluginTags.tags.design'),
},
{
name: 'travel',
label: t('pluginTags.travel'),
label: t('pluginTags.tags.travel'),
},
{
name: 'social',
label: t('pluginTags.social'),
label: t('pluginTags.tags.social'),
},
{
name: 'news',
label: t('pluginTags.news'),
label: t('pluginTags.tags.news'),
},
{
name: 'medical',
label: t('pluginTags.medical'),
label: t('pluginTags.tags.medical'),
},
{
name: 'productivity',
label: t('pluginTags.productivity'),
label: t('pluginTags.tags.productivity'),
},
{
name: 'education',
label: t('pluginTags.education'),
label: t('pluginTags.tags.education'),
},
{
name: 'business',
label: t('pluginTags.business'),
label: t('pluginTags.tags.business'),
},
{
name: 'entertainment',
label: t('pluginTags.entertainment'),
label: t('pluginTags.tags.entertainment'),
},
{
name: 'utilities',
label: t('pluginTags.utilities'),
label: t('pluginTags.tags.utilities'),
},
{
name: 'other',
label: t('pluginTags.other'),
label: t('pluginTags.tags.other'),
},
]
const tagsMap = tags.reduce((acc, tag) => {
acc[tag.name] = tag
return acc
}, {} as Record<string, Tag>)
return {
tags,
tagsMap,
}
}

View File

@ -1,6 +1,7 @@
'use client'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiArrowDownSLine,
RiCloseCircleFill,
@ -26,9 +27,10 @@ const TagsFilter = ({
onTagsChange,
size,
}: TagsFilterProps) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const [searchText, setSearchText] = useState('')
const options = useTags()
const { tags: options, tagsMap } = useTags()
const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchText.toLowerCase()))
const handleCheck = (id: string) => {
if (tags.includes(id))
@ -65,10 +67,10 @@ const TagsFilter = ({
size === 'small' && 'px-0.5 py-1',
)}>
{
!selectedTagsLength && 'All Tags'
!selectedTagsLength && t('pluginTags.allTags')
}
{
!!selectedTagsLength && tags.slice(0, 2).join(',')
!!selectedTagsLength && tags.map(tag => tagsMap[tag].label).slice(0, 2).join(',')
}
{
selectedTagsLength > 2 && (
@ -100,7 +102,7 @@ const TagsFilter = ({
showLeftIcon
value={searchText}
onChange={e => setSearchText(e.target.value)}
placeholder='Search tags'
placeholder={t('pluginTags.searchTags') || ''}
/>
</div>
<div className='p-1 max-h-[448px] overflow-y-auto'>

View File

@ -7,7 +7,7 @@ import {
RiHardDrive3Line,
RiVerifiedBadgeLine,
} from '@remixicon/react'
import type { InstalledPlugin } from '../types'
import type { PluginDetail } from '../types'
import { PluginSource } from '../types'
import Description from '../card/base/description'
import Icon from '../card/base/card-icon'
@ -30,7 +30,7 @@ import cn from '@/utils/classnames'
const i18nPrefix = 'plugin.action'
type Props = {
detail: InstalledPlugin
detail: PluginDetail
onHide: () => void
onDelete: () => void
}

View File

@ -10,6 +10,9 @@ import Badge, { BadgeState } from '@/app/components/base/badge/index'
import type { UpdateFromMarketPlacePayload } from '../types'
import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
import useGetIcon from '../install-plugin/base/use-get-icon'
import { updateFromMarketPlace } from '@/service/plugins'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/store'
const i18nPrefix = 'plugin.upgrade'
@ -43,7 +46,18 @@ const UpdatePluginModal: FC<Props> = ({
setIcon(icon)
})()
}, [originalPackageInfo, getIconUrl])
const {
check,
stop,
} = checkTaskStatus()
const handleCancel = () => {
stop()
onCancel()
}
const [uploadStep, setUploadStep] = useState<UploadStep>(UploadStep.notStarted)
const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling)
const configBtnText = useMemo(() => {
return ({
[UploadStep.notStarted]: t(`${i18nPrefix}.upgrade`),
@ -52,19 +66,41 @@ const UpdatePluginModal: FC<Props> = ({
})[uploadStep]
}, [t, uploadStep])
const handleConfirm = useCallback(() => {
const handleConfirm = useCallback(async () => {
if (uploadStep === UploadStep.notStarted) {
setUploadStep(UploadStep.upgrading)
setTimeout(() => {
setUploadStep(UploadStep.installed)
}, 1500)
return
const {
all_installed: isInstalled,
task_id: taskId,
} = await updateFromMarketPlace({
original_plugin_unique_identifier: originalPackageInfo.id,
new_plugin_unique_identifier: targetPackageInfo.id,
})
if (isInstalled) {
onSave()
return
}
setPluginTasksWithPolling()
await check({
taskId,
pluginUniqueIdentifier: targetPackageInfo.id,
})
onSave()
}
if (uploadStep === UploadStep.installed) {
onSave()
onCancel()
}
}, [onCancel, onSave, uploadStep])
}, [onCancel, onSave, uploadStep, check, originalPackageInfo.id, setPluginTasksWithPolling, targetPackageInfo.id])
const usedInAppInfo = useMemo(() => {
return (
<div className='flex px-0.5 justify-center items-center gap-0.5'>
<div className='text-text-warning system-xs-medium'>{t(`${i18nPrefix}.usedInApps`, { num: 3 })}</div>
{/* show the used apps */}
<RiInformation2Line className='w-4 h-4 text-text-tertiary' />
</div>
)
}, [t])
return (
<Modal
isShow={true}
@ -89,11 +125,7 @@ const UpdatePluginModal: FC<Props> = ({
<Badge className='mx-1' size="s" state={BadgeState.Warning}>
{`${originalPackageInfo.payload.version} -> ${targetPackageInfo.version}`}
</Badge>
<div className='flex px-0.5 justify-center items-center gap-0.5'>
<div className='text-text-warning system-xs-medium'>{t(`${i18nPrefix}.usedInApps`, { num: 3 })}</div>
{/* show the used apps */}
<RiInformation2Line className='w-4 h-4 text-text-tertiary' />
</div>
{false && usedInAppInfo}
</>
}
/>
@ -101,7 +133,7 @@ const UpdatePluginModal: FC<Props> = ({
<div className='flex pt-5 justify-end items-center gap-2 self-stretch'>
{uploadStep === UploadStep.notStarted && (
<Button
onClick={onCancel}
onClick={handleCancel}
>
{t('common.operation.cancel')}
</Button>

View File

@ -27,7 +27,7 @@ const LabelFilter: FC<LabelFilterProps> = ({
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const labelList = useTags()
const { tags: labelList } = useTags()
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')

View File

@ -26,7 +26,7 @@ const LabelSelector: FC<LabelSelectorProps> = ({
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const labelList = useTags()
const { tags: labelList } = useTags()
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')

View File

@ -66,7 +66,7 @@ const AllTools = ({
const {
queryPluginsWithDebounced: fetchPlugins,
plugins: notInstalledPlugins,
plugins: notInstalledPlugins = [],
} = useMarketplacePlugins()
useEffect(() => {

View File

@ -1,20 +1,24 @@
const translation = {
search: 'Search',
image: 'Image',
videos: 'Videos',
weather: 'Weather',
finance: 'Finance',
design: 'Design',
travel: 'Travel',
social: 'Social',
news: 'News',
medical: 'Medical',
productivity: 'Productivity',
education: 'Education',
business: 'Business',
entertainment: 'Entertainment',
utilities: 'Utilities',
other: 'Other',
allTags: 'All Tags',
searchTags: 'Search Tags',
tags: {
search: 'Search',
image: 'Image',
videos: 'Videos',
weather: 'Weather',
finance: 'Finance',
design: 'Design',
travel: 'Travel',
social: 'Social',
news: 'News',
medical: 'Medical',
productivity: 'Productivity',
education: 'Education',
business: 'Business',
entertainment: 'Entertainment',
utilities: 'Utilities',
other: 'Other',
},
}
export default translation

View File

@ -1,20 +1,24 @@
const translation = {
search: '搜索',
image: '图片',
videos: '视频',
weather: '天气',
finance: '金融',
design: '设计',
travel: '旅行',
social: '社交',
news: '新闻',
medical: '医疗',
productivity: '生产力',
education: '教育',
business: '商业',
entertainment: '娱乐',
utilities: '工具',
other: '其他',
allTags: '所有标签',
searchTags: '搜索标签',
tags: {
search: '搜索',
image: '图片',
videos: '视频',
weather: '天气',
finance: '金融',
design: '设计',
travel: '旅行',
social: '社交',
news: '新闻',
medical: '医疗',
productivity: '生产力',
education: '教育',
business: '商业',
entertainment: '娱乐',
utilities: '工具',
other: '其他',
},
}
export default translation

View File

@ -71,6 +71,12 @@ export const installPackageFromLocal = async (uniqueIdentifier: string) => {
})
}
export const updateFromMarketPlace = async (body: Record<string, string>) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/upgrade/marketplace', {
body,
})
}
export const uploadGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string) => {
return post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', {
body: {