feat: add deprecation notice component (#22706)

pull/22685/head
Wu Tianwei 9 months ago committed by GitHub
parent 35951a7def
commit b0fc4a594c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,76 @@
import React from 'react'
import type { FC } from 'react'
import Link from 'next/link'
import cn from '@/utils/classnames'
import { RiAlertFill } from '@remixicon/react'
import { Trans, useTranslation } from 'react-i18next'
type DeprecationNoticeProps = {
status: 'deleted' | 'active'
deprecatedReason: string
alternativePluginId: string
urlPrefix: string
className?: string
innerWrapperClassName?: string
iconWrapperClassName?: string
textClassName?: string
}
const DeprecationNotice: FC<DeprecationNoticeProps> = ({
status,
deprecatedReason,
alternativePluginId,
urlPrefix,
className,
innerWrapperClassName,
iconWrapperClassName,
textClassName,
}) => {
const { t } = useTranslation()
if (status !== 'deleted' || !deprecatedReason)
return null
return (
<div className={cn('w-full', className)}>
<div className={cn(
'relative flex items-start gap-x-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]',
innerWrapperClassName,
)}>
<div className='absolute left-0 top-0 -z-10 h-full w-full bg-toast-warning-bg opacity-40' />
<div className={cn('flex size-6 shrink-0 items-center justify-center', iconWrapperClassName)}>
<RiAlertFill className='size-4 text-text-warning-secondary' />
</div>
<div className={cn('system-xs-regular grow py-1 text-text-primary', textClassName)}>
{
alternativePluginId ? (
<Trans
i18nKey={'plugin.detailPanel.deprecation.fullMessage'}
components={{
CustomLink: (
<Link
href={`${urlPrefix}/plugins/${alternativePluginId}`}
target='_blank'
rel='noopener noreferrer'
className='underline'
/>
),
}}
values={{
deprecatedReason,
alternativePluginId,
}}
/>
) : (
<span>
{t('plugin.detailPanel.deprecation.onlyReason', { deprecatedReason })}
</span>
)
}
</div>
</div>
</div>
)
}
export default React.memo(DeprecationNotice)

@ -33,12 +33,13 @@ import { useGetLanguage } from '@/context/i18n'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { useInvalidateAllToolProviders } from '@/service/use-tools'
import { API_PREFIX } from '@/config'
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
import cn from '@/utils/classnames'
import { getMarketplaceUrl } from '@/utils/var'
import { PluginAuth } from '@/app/components/plugins/plugin-auth'
import { AuthCategory } from '@/app/components/plugins/plugin-auth'
import { useAllToolProviders } from '@/service/use-tools'
import DeprecationNotice from '../base/deprecation-notice'
const i18nPrefix = 'plugin.action'
@ -70,6 +71,9 @@ const DetailHeader = ({
latest_version,
meta,
plugin_id,
status,
deprecated_reason,
alternative_plugin_id,
} = detail
const { author, category, name, label, description, icon, verified, tool } = detail.declaration
const isTool = category === PluginType.tool
@ -272,6 +276,13 @@ const DetailHeader = ({
</ActionButton>
</div>
</div>
<DeprecationNotice
status={status}
deprecatedReason={deprecated_reason}
alternativePluginId={alternative_plugin_id}
urlPrefix={MARKETPLACE_URL_PREFIX}
className='mt-3'
/>
<Description className='mb-2 mt-3 h-auto' text={description[locale]} descriptionLineRows={2}></Description>
{
category === PluginType.tool && (

@ -1,6 +1,6 @@
'use client'
import type { FC } from 'react'
import React, { useMemo } from 'react'
import React, { useCallback, useMemo } from 'react'
import { useTheme } from 'next-themes'
import {
RiArrowRightUpLine,
@ -55,6 +55,8 @@ const PluginItem: FC<Props> = ({
endpoints_active,
meta,
plugin_id,
status,
deprecated_reason,
} = plugin
const { category, author, name, label, description, icon, verified, meta: declarationMeta } = plugin.declaration
@ -70,9 +72,14 @@ const PluginItem: FC<Props> = ({
return gte(langeniusVersionInfo.current_version, declarationMeta.minimum_dify_version ?? '0.0.0')
}, [declarationMeta.minimum_dify_version, langeniusVersionInfo.current_version])
const handleDelete = () => {
const isDeprecated = useMemo(() => {
return status === 'deleted' && !!deprecated_reason
}, [status, deprecated_reason])
const handleDelete = useCallback(() => {
refreshPluginList({ category } as any)
}
}, [category, refreshPluginList])
const getValueFromI18nObject = useRenderI18nObject()
const title = getValueFromI18nObject(label)
const descriptionText = getValueFromI18nObject(description)
@ -81,7 +88,7 @@ const PluginItem: FC<Props> = ({
return (
<div
className={cn(
'rounded-xl border-[1.5px] border-background-section-burn p-1',
'relative overflow-hidden rounded-xl border-[1.5px] border-background-section-burn p-1',
currentPluginID === plugin_id && 'border-components-option-card-option-selected-border',
source === PluginSource.debugging
? 'bg-[repeating-linear-gradient(-45deg,rgba(16,24,40,0.04),rgba(16,24,40,0.04)_5px,rgba(0,0,0,0.02)_5px,rgba(0,0,0,0.02)_10px)]'
@ -91,10 +98,10 @@ const PluginItem: FC<Props> = ({
setCurrentPluginID(plugin.plugin_id)
}}
>
<div className={cn('hover-bg-components-panel-on-panel-item-bg relative rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs', className)}>
<div className={cn('hover-bg-components-panel-on-panel-item-bg relative z-10 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs', className)}>
<CornerMark text={categoriesMap[category].label} />
{/* Header */}
<div className="flex">
<div className='flex'>
<div className='flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border-[1px] border-components-panel-border-subtle'>
<img
className='h-full w-full'
@ -102,13 +109,13 @@ const PluginItem: FC<Props> = ({
alt={`plugin-${plugin_unique_identifier}-logo`}
/>
</div>
<div className="ml-3 w-0 grow">
<div className="flex h-5 items-center">
<div className='ml-3 w-0 grow'>
<div className='flex h-5 items-center'>
<Title title={title} />
{verified && <RiVerifiedBadgeLine className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" />}
{verified && <RiVerifiedBadgeLine className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' />}
{!isDifyVersionCompatible && <Tooltip popupContent={
t('plugin.difyVersionNotCompatible', { minimalDifyVersion: declarationMeta.minimum_dify_version })
}><RiErrorWarningLine color='red' className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" /></Tooltip>}
}><RiErrorWarningLine color='red' className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' /></Tooltip>}
<Badge className='ml-1 shrink-0'
text={source === PluginSource.github ? plugin.meta!.version : plugin.version}
hasRedCornerMark={(source === PluginSource.marketplace) && !!plugin.latest_version && plugin.latest_version !== plugin.version}
@ -135,10 +142,11 @@ const PluginItem: FC<Props> = ({
</div>
</div>
</div>
<div className='mb-1 mt-1.5 flex h-4 items-center justify-between px-4'>
<div className='flex items-center'>
<div className='mb-1 mt-1.5 flex h-4 items-center gap-x-2 px-4'>
{/* Organization & Name */}
<div className='flex grow items-center overflow-hidden'>
<OrgInfo
className="mt-0.5"
className='mt-0.5'
orgName={orgName}
packageName={name}
packageNameClassName='w-auto max-w-[150px]'
@ -146,15 +154,20 @@ const PluginItem: FC<Props> = ({
{category === PluginType.extension && (
<>
<div className='system-xs-regular mx-2 text-text-quaternary'>·</div>
<div className='system-xs-regular flex space-x-1 text-text-tertiary'>
<RiLoginCircleLine className='h-4 w-4' />
<span>{t('plugin.endpointsEnabled', { num: endpoints_active })}</span>
<div className='system-xs-regular flex space-x-1 overflow-hidden text-text-tertiary'>
<RiLoginCircleLine className='h-4 w-4 shrink-0' />
<span
className='truncate'
title={t('plugin.endpointsEnabled', { num: endpoints_active })}
>
{t('plugin.endpointsEnabled', { num: endpoints_active })}
</span>
</div>
</>
)}
</div>
<div className='flex items-center'>
{/* Source */}
<div className='flex shrink-0 items-center'>
{source === PluginSource.github
&& <>
<a href={`https://github.com/${meta!.repo}`} target='_blank' className='flex items-center gap-1'>
@ -192,7 +205,20 @@ const PluginItem: FC<Props> = ({
</>
}
</div>
{/* Deprecated */}
{source === PluginSource.marketplace && enable_marketplace && isDeprecated && (
<div className='system-2xs-medium-uppercase flex shrink-0 items-center gap-x-2'>
<span className='text-text-tertiary'>·</span>
<span className='text-text-warning'>
{t('plugin.deprecated')}
</span>
</div>
)}
</div>
{/* BG Effect for Deprecated Plugin */}
{source === PluginSource.marketplace && enable_marketplace && isDeprecated && (
<div className='absolute bottom-[-71px] right-[-45px] z-0 size-40 bg-components-badge-status-light-warning-halo opacity-60 blur-[120px]' />
)}
</div>
)
}

@ -36,6 +36,9 @@ const PluginsPanel = () => {
...plugin,
latest_version: installedLatestVersion?.versions[plugin.plugin_id]?.version ?? '',
latest_unique_identifier: installedLatestVersion?.versions[plugin.plugin_id]?.unique_identifier ?? '',
status: installedLatestVersion?.versions[plugin.plugin_id]?.status ?? 'active',
deprecated_reason: installedLatestVersion?.versions[plugin.plugin_id]?.deprecated_reason ?? '',
alternative_plugin_id: installedLatestVersion?.versions[plugin.plugin_id]?.alternative_plugin_id ?? '',
})) || []
}, [pluginList, installedLatestVersion])
@ -66,20 +69,25 @@ const PluginsPanel = () => {
onFilterChange={handleFilterChange}
/>
</div>
{isPluginListLoading ? <Loading type='app' /> : (filteredList?.length ?? 0) > 0 ? (
<div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'>
<div className='w-full'>
<List pluginList={filteredList || []} />
</div>
{!isLastPage && !isFetching && (
<Button onClick={loadNextPage}>
{t('workflow.common.loadMore')}
</Button>
{isPluginListLoading && <Loading type='app' />}
{!isPluginListLoading && (
<>
{(filteredList?.length ?? 0) > 0 ? (
<div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'>
<div className='w-full'>
<List pluginList={filteredList || []} />
</div>
{!isLastPage && !isFetching && (
<Button onClick={loadNextPage}>
{t('workflow.common.loadMore')}
</Button>
)}
{isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>}
</div>
) : (
<Empty />
)}
{isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>}
</div>
) : (
<Empty />
</>
)}
<PluginDetailPanel
detail={currentPluginDetail}

@ -118,6 +118,9 @@ export type PluginDetail = {
latest_unique_identifier: string
source: PluginSource
meta?: MetaData
status: 'active' | 'deleted'
deprecated_reason: string
alternative_plugin_id: string
}
export type PluginInfoFromMarketPlace = {
@ -343,6 +346,9 @@ export type InstalledLatestVersionResponse = {
[plugin_id: string]: {
unique_identifier: string
version: string
status: 'active' | 'deleted'
deprecated_reason: string
alternative_plugin_id: string
} | null
}
}

@ -29,6 +29,7 @@ const translation = {
searchTools: 'Search tools...',
installPlugin: 'Install plugin',
installFrom: 'INSTALL FROM',
deprecated: 'Deprecated',
list: {
noInstalled: 'No plugins installed',
notFound: 'No plugins found',
@ -99,6 +100,10 @@ const translation = {
configureApp: 'Configure App',
configureModel: 'Configure model',
configureTool: 'Configure tool',
deprecation: {
fullMessage: 'This plugin has been deprecated due to {{deprecatedReason}}, please use <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> instead.',
onlyReason: 'This plugin has been deprecated due to {{deprecatedReason}}.',
},
},
install: '{{num}} installs',
installAction: 'Install',

@ -29,6 +29,7 @@ const translation = {
searchTools: '搜索工具...',
installPlugin: '安装插件',
installFrom: '安装源',
deprecated: '已弃用',
list: {
noInstalled: '无已安装的插件',
notFound: '未找到插件',
@ -99,6 +100,10 @@ const translation = {
configureApp: '应用设置',
configureModel: '模型设置',
configureTool: '工具设置',
deprecation: {
fullMessage: '此插件已被弃用,原因是{{deprecatedReason}},请使用 <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> 替代。',
onlyReason: '此插件已被弃用,原因是{{deprecatedReason}}。',
},
},
install: '{{num}} 次安装',
installAction: '安装',

Loading…
Cancel
Save