mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
Added a utility function to generate plugin card icon URLs based on the plugin's source and workspace context. Updated the Card component to utilize this function for determining the correct icon source. Enhanced unit tests to verify the correct URL generation for both marketplace and package icons.
121 lines
4.1 KiB
TypeScript
121 lines
4.1 KiB
TypeScript
'use client'
|
|
import type { Plugin } from '../types'
|
|
import { useTranslation } from '#i18n'
|
|
import { RiAlertFill } from '@remixicon/react'
|
|
import * as React from 'react'
|
|
import { useSelector } from '@/context/app-context'
|
|
import { useGetLanguage } from '@/context/i18n'
|
|
import useTheme from '@/hooks/use-theme'
|
|
import {
|
|
renderI18nObject,
|
|
} from '@/i18n-config'
|
|
import { Theme } from '@/types/app'
|
|
import { cn } from '@/utils/classnames'
|
|
import Partner from '../base/badges/partner'
|
|
import Verified from '../base/badges/verified'
|
|
import Icon from '../card/base/card-icon'
|
|
import { useCategories } from '../hooks'
|
|
import { getPluginCardIconUrl } from '../utils'
|
|
import CornerMark from './base/corner-mark'
|
|
import Description from './base/description'
|
|
import OrgInfo from './base/org-info'
|
|
import Placeholder from './base/placeholder'
|
|
import Title from './base/title'
|
|
|
|
export type Props = {
|
|
className?: string
|
|
payload: Plugin
|
|
titleLeft?: React.ReactNode
|
|
installed?: boolean
|
|
installFailed?: boolean
|
|
hideCornerMark?: boolean
|
|
descriptionLineRows?: number
|
|
footer?: React.ReactNode
|
|
isLoading?: boolean
|
|
loadingFileName?: string
|
|
limitedInstall?: boolean
|
|
}
|
|
|
|
const Card = ({
|
|
className,
|
|
payload,
|
|
titleLeft,
|
|
installed,
|
|
installFailed,
|
|
hideCornerMark,
|
|
descriptionLineRows = 2,
|
|
footer,
|
|
isLoading = false,
|
|
loadingFileName,
|
|
limitedInstall = false,
|
|
}: Props) => {
|
|
const locale = useGetLanguage()
|
|
const { t } = useTranslation()
|
|
const { categoriesMap } = useCategories(true)
|
|
const currentWorkspaceId = useSelector(s => s.currentWorkspace.id)
|
|
const { category, type, name, org, label, brief, icon, icon_dark, verified, badges = [], from } = payload
|
|
const { theme } = useTheme()
|
|
const iconSrc = getPluginCardIconUrl(
|
|
{ from, name, org, type },
|
|
theme === Theme.dark && icon_dark ? icon_dark : icon,
|
|
currentWorkspaceId,
|
|
)
|
|
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
|
obj ? renderI18nObject(obj, locale) : ''
|
|
const isPartner = badges.includes('partner')
|
|
|
|
const wrapClassName = cn('hover-bg-components-panel-on-panel-item-bg relative overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', className)
|
|
if (isLoading) {
|
|
return (
|
|
<Placeholder
|
|
wrapClassName={wrapClassName}
|
|
loadingFileName={loadingFileName!}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className={wrapClassName}>
|
|
<div className={cn('p-4 pb-3', limitedInstall && 'pb-1')}>
|
|
{!hideCornerMark && <CornerMark text={categoriesMap[type === 'bundle' ? type : category]?.label} />}
|
|
{/* Header */}
|
|
<div className="flex">
|
|
<Icon src={iconSrc} installed={installed} installFailed={installFailed} />
|
|
<div className="ml-3 w-0 grow">
|
|
<div className="flex h-5 items-center">
|
|
<Title title={getLocalizedText(label)} />
|
|
{isPartner && <Partner className="ml-0.5 h-4 w-4" text={t('marketplace.partnerTip', { ns: 'plugin' })} />}
|
|
{verified && <Verified className="ml-0.5 h-4 w-4" text={t('marketplace.verifiedTip', { ns: 'plugin' })} />}
|
|
{titleLeft}
|
|
{' '}
|
|
{/* This can be version badge */}
|
|
</div>
|
|
<OrgInfo
|
|
className="mt-0.5"
|
|
orgName={org}
|
|
packageName={name}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<Description
|
|
className="mt-3"
|
|
text={getLocalizedText(brief)}
|
|
descriptionLineRows={descriptionLineRows}
|
|
/>
|
|
{!!footer && <div>{footer}</div>}
|
|
</div>
|
|
{limitedInstall
|
|
&& (
|
|
<div className="relative flex h-8 items-center gap-x-2 px-3 after:absolute after:bottom-0 after:left-0 after:right-0 after:top-0 after:bg-toast-warning-bg after:opacity-40">
|
|
<RiAlertFill className="h-3 w-3 shrink-0 text-text-warning-secondary" />
|
|
<p className="z-10 grow text-text-secondary system-xs-regular">
|
|
{t('installModal.installWarning', { ns: 'plugin' })}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default React.memo(Card)
|