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

pull/12372/head
Yi 2 years ago
commit 61a70e7a71

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

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

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

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

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

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

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

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

@ -1,4 +1,7 @@
const translation = { const translation = {
allTags: 'All Tags',
searchTags: 'Search Tags',
tags: {
search: 'Search', search: 'Search',
image: 'Image', image: 'Image',
videos: 'Videos', videos: 'Videos',
@ -15,6 +18,7 @@ const translation = {
entertainment: 'Entertainment', entertainment: 'Entertainment',
utilities: 'Utilities', utilities: 'Utilities',
other: 'Other', other: 'Other',
},
} }
export default translation export default translation

@ -1,4 +1,7 @@
const translation = { const translation = {
allTags: '所有标签',
searchTags: '搜索标签',
tags: {
search: '搜索', search: '搜索',
image: '图片', image: '图片',
videos: '视频', videos: '视频',
@ -15,6 +18,7 @@ const translation = {
entertainment: '娱乐', entertainment: '娱乐',
utilities: '工具', utilities: '工具',
other: '其他', other: '其他',
},
} }
export default translation export default translation

@ -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) => { export const uploadGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string) => {
return post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', { return post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', {
body: { body: {

Loading…
Cancel
Save