switch-plugin-version.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. 'use client'
  2. import Badge from '@/app/components/base/badge'
  3. import Tooltip from '@/app/components/base/tooltip'
  4. import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
  5. import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react'
  6. import type { ReactNode } from 'react'
  7. import { type FC, useCallback, useState } from 'react'
  8. import { useBoolean } from 'ahooks'
  9. import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
  10. import cn from '@/utils/classnames'
  11. import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model'
  12. import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
  13. import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
  14. import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index'
  15. import Link from 'next/link'
  16. import { useTranslation } from 'react-i18next'
  17. import { getMarketplaceUrl } from '@/utils/var'
  18. export type SwitchPluginVersionProps = {
  19. uniqueIdentifier: string
  20. tooltip?: ReactNode
  21. onChange?: (version: string) => void
  22. className?: string
  23. }
  24. export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
  25. const { uniqueIdentifier, tooltip, onChange, className } = props
  26. const [pluginId] = uniqueIdentifier?.split(':') || ['']
  27. const [isShow, setIsShow] = useState(false)
  28. const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal }] = useBoolean(false)
  29. const [target, setTarget] = useState<{
  30. version: string,
  31. pluginUniqueIden: string;
  32. }>()
  33. const pluginDetails = useCheckInstalled({
  34. pluginIds: [pluginId],
  35. enabled: true,
  36. })
  37. const pluginDetail = pluginDetails.data?.plugins.at(0)
  38. const handleUpdatedFromMarketplace = useCallback(() => {
  39. hideUpdateModal()
  40. pluginDetails.refetch()
  41. onChange?.(target!.version)
  42. }, [hideUpdateModal, onChange, pluginDetails, target])
  43. const { getIconUrl } = useGetIcon()
  44. const icon = pluginDetail?.declaration.icon ? getIconUrl(pluginDetail.declaration.icon) : undefined
  45. const mutation = useUpdatePackageFromMarketPlace()
  46. const install = () => {
  47. mutation.mutate(
  48. {
  49. new_plugin_unique_identifier: target!.pluginUniqueIden,
  50. original_plugin_unique_identifier: uniqueIdentifier,
  51. },
  52. {
  53. onSuccess() {
  54. handleUpdatedFromMarketplace()
  55. },
  56. })
  57. }
  58. const { t } = useTranslation()
  59. // Guard against null/undefined uniqueIdentifier to prevent app crash
  60. if (!uniqueIdentifier || !pluginId)
  61. return null
  62. return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'>
  63. <div className={cn('flex w-fit items-center justify-center', className)} onClick={e => e.stopPropagation()}>
  64. {isShowUpdateModal && pluginDetail && <PluginMutationModel
  65. onCancel={hideUpdateModal}
  66. plugin={pluginManifestToCardPluginProps({
  67. ...pluginDetail.declaration,
  68. icon: icon!,
  69. })}
  70. mutation={mutation}
  71. mutate={install}
  72. confirmButtonText={t('workflow.nodes.agent.installPlugin.install')}
  73. cancelButtonText={t('workflow.nodes.agent.installPlugin.cancel')}
  74. modelTitle={t('workflow.nodes.agent.installPlugin.title')}
  75. description={t('workflow.nodes.agent.installPlugin.desc')}
  76. cardTitleLeft={<>
  77. <Badge2 className='mx-1' size="s" state={BadgeState.Warning}>
  78. {`${pluginDetail.version} -> ${target!.version}`}
  79. </Badge2>
  80. </>}
  81. modalBottomLeft={
  82. <Link
  83. className='flex items-center justify-center gap-1'
  84. href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)}
  85. target='_blank'
  86. >
  87. <span className='system-xs-regular text-xs text-text-accent'>
  88. {t('workflow.nodes.agent.installPlugin.changelog')}
  89. </span>
  90. <RiExternalLinkLine className='size-3 text-text-accent' />
  91. </Link>
  92. }
  93. />}
  94. {pluginDetail && <PluginVersionPicker
  95. isShow={isShow}
  96. onShowChange={setIsShow}
  97. pluginID={pluginId}
  98. currentVersion={pluginDetail.version}
  99. onSelect={(state) => {
  100. setTarget({
  101. pluginUniqueIden: state.unique_identifier,
  102. version: state.version,
  103. })
  104. showUpdateModal()
  105. }}
  106. trigger={
  107. <Badge
  108. className={cn(
  109. 'mx-1 flex hover:bg-state-base-hover',
  110. isShow && 'bg-state-base-hover',
  111. )}
  112. uppercase={true}
  113. text={
  114. <>
  115. <div>{pluginDetail.version}</div>
  116. <RiArrowLeftRightLine className='ml-1 h-3 w-3 text-text-tertiary' />
  117. </>
  118. }
  119. hasRedCornerMark={true}
  120. />
  121. }
  122. />}
  123. </div>
  124. </Tooltip>
  125. }