from-market-place.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  4. import { RiInformation2Line } from '@remixicon/react'
  5. import { useTranslation } from 'react-i18next'
  6. import Card from '@/app/components/plugins/card'
  7. import Modal from '@/app/components/base/modal'
  8. import Button from '@/app/components/base/button'
  9. import Badge, { BadgeState } from '@/app/components/base/badge/index'
  10. import { TaskStatus, type UpdateFromMarketPlacePayload } from '../types'
  11. import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
  12. import useGetIcon from '../install-plugin/base/use-get-icon'
  13. import { updateFromMarketPlace } from '@/service/plugins'
  14. import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
  15. import { usePluginTaskList } from '@/service/use-plugins'
  16. import Toast from '../../base/toast'
  17. const i18nPrefix = 'plugin.upgrade'
  18. type Props = {
  19. payload: UpdateFromMarketPlacePayload
  20. onSave: () => void
  21. onCancel: () => void
  22. }
  23. enum UploadStep {
  24. notStarted = 'notStarted',
  25. upgrading = 'upgrading',
  26. installed = 'installed',
  27. }
  28. const UpdatePluginModal: FC<Props> = ({
  29. payload,
  30. onSave,
  31. onCancel,
  32. }) => {
  33. const {
  34. originalPackageInfo,
  35. targetPackageInfo,
  36. } = payload
  37. const { t } = useTranslation()
  38. const { getIconUrl } = useGetIcon()
  39. const [icon, setIcon] = useState<string>(originalPackageInfo.payload.icon)
  40. useEffect(() => {
  41. (async () => {
  42. const icon = await getIconUrl(originalPackageInfo.payload.icon)
  43. setIcon(icon)
  44. })()
  45. }, [originalPackageInfo, getIconUrl])
  46. const {
  47. check,
  48. stop,
  49. } = checkTaskStatus()
  50. const handleCancel = () => {
  51. stop()
  52. onCancel()
  53. }
  54. const [uploadStep, setUploadStep] = useState<UploadStep>(UploadStep.notStarted)
  55. const { handleRefetch } = usePluginTaskList(payload.category)
  56. const configBtnText = useMemo(() => {
  57. return ({
  58. [UploadStep.notStarted]: t(`${i18nPrefix}.upgrade`),
  59. [UploadStep.upgrading]: t(`${i18nPrefix}.upgrading`),
  60. [UploadStep.installed]: t(`${i18nPrefix}.close`),
  61. })[uploadStep]
  62. }, [t, uploadStep])
  63. const handleConfirm = useCallback(async () => {
  64. if (uploadStep === UploadStep.notStarted) {
  65. setUploadStep(UploadStep.upgrading)
  66. try {
  67. const {
  68. all_installed: isInstalled,
  69. task_id: taskId,
  70. } = await updateFromMarketPlace({
  71. original_plugin_unique_identifier: originalPackageInfo.id,
  72. new_plugin_unique_identifier: targetPackageInfo.id,
  73. })
  74. if (isInstalled) {
  75. onSave()
  76. return
  77. }
  78. handleRefetch()
  79. const { status, error } = await check({
  80. taskId,
  81. pluginUniqueIdentifier: targetPackageInfo.id,
  82. })
  83. if (status === TaskStatus.failed) {
  84. Toast.notify({ type: 'error', message: error! })
  85. return
  86. }
  87. onSave()
  88. }
  89. // eslint-disable-next-line unused-imports/no-unused-vars
  90. catch (e) {
  91. setUploadStep(UploadStep.notStarted)
  92. }
  93. return
  94. }
  95. if (uploadStep === UploadStep.installed)
  96. onSave()
  97. }, [onSave, uploadStep, check, originalPackageInfo.id, handleRefetch, targetPackageInfo.id])
  98. const usedInAppInfo = useMemo(() => {
  99. return (
  100. <div className='flex px-0.5 justify-center items-center gap-0.5'>
  101. <div className='text-text-warning system-xs-medium'>{t(`${i18nPrefix}.usedInApps`, { num: 3 })}</div>
  102. {/* show the used apps */}
  103. <RiInformation2Line className='w-4 h-4 text-text-tertiary' />
  104. </div>
  105. )
  106. }, [t])
  107. return (
  108. <Modal
  109. isShow={true}
  110. onClose={onCancel}
  111. className='min-w-[560px]'
  112. closable
  113. title={t(`${i18nPrefix}.${uploadStep === UploadStep.installed ? 'successfulTitle' : 'title'}`)}
  114. >
  115. <div className='mt-3 mb-2 text-text-secondary system-md-regular'>
  116. {t(`${i18nPrefix}.description`)}
  117. </div>
  118. <div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'>
  119. <Card
  120. installed={uploadStep === UploadStep.installed}
  121. payload={pluginManifestToCardPluginProps({
  122. ...originalPackageInfo.payload,
  123. icon: icon!,
  124. })}
  125. className='w-full'
  126. titleLeft={
  127. <>
  128. <Badge className='mx-1' size="s" state={BadgeState.Warning}>
  129. {`${originalPackageInfo.payload.version} -> ${targetPackageInfo.version}`}
  130. </Badge>
  131. {false && usedInAppInfo}
  132. </>
  133. }
  134. />
  135. </div>
  136. <div className='flex pt-5 justify-end items-center gap-2 self-stretch'>
  137. {uploadStep === UploadStep.notStarted && (
  138. <Button
  139. onClick={handleCancel}
  140. >
  141. {t('common.operation.cancel')}
  142. </Button>
  143. )}
  144. <Button
  145. variant='primary'
  146. loading={uploadStep === UploadStep.upgrading}
  147. onClick={handleConfirm}
  148. disabled={uploadStep === UploadStep.upgrading}
  149. >
  150. {configBtnText}
  151. </Button>
  152. </div>
  153. </Modal>
  154. )
  155. }
  156. export default React.memo(UpdatePluginModal)