loaded.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. 'use client'
  2. import React, { useEffect } from 'react'
  3. import Button from '@/app/components/base/button'
  4. import { type Plugin, type PluginDeclaration, TaskStatus, type UpdateFromGitHubPayload } from '../../../types'
  5. import Card from '../../../card'
  6. import { pluginManifestToCardPluginProps } from '../../utils'
  7. import { useTranslation } from 'react-i18next'
  8. import { updateFromGitHub } from '@/service/plugins'
  9. import { useInstallPackageFromGitHub } from '@/service/use-plugins'
  10. import { RiLoader2Line } from '@remixicon/react'
  11. import { usePluginTaskList } from '@/service/use-plugins'
  12. import checkTaskStatus from '../../base/check-task-status'
  13. import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
  14. import { parseGitHubUrl } from '../../utils'
  15. import Version from '../../base/version'
  16. type LoadedProps = {
  17. updatePayload: UpdateFromGitHubPayload
  18. uniqueIdentifier: string
  19. payload: PluginDeclaration | Plugin
  20. repoUrl: string
  21. selectedVersion: string
  22. selectedPackage: string
  23. onBack: () => void
  24. onStartToInstall?: () => void
  25. onInstalled: (notRefresh?: boolean) => void
  26. onFailed: (message?: string) => void
  27. }
  28. const i18nPrefix = 'plugin.installModal'
  29. const Loaded: React.FC<LoadedProps> = ({
  30. updatePayload,
  31. uniqueIdentifier,
  32. payload,
  33. repoUrl,
  34. selectedVersion,
  35. selectedPackage,
  36. onBack,
  37. onStartToInstall,
  38. onInstalled,
  39. onFailed,
  40. }) => {
  41. const { t } = useTranslation()
  42. const toInstallVersion = payload.version
  43. const pluginId = (payload as Plugin).plugin_id
  44. const { installedInfo, isLoading } = useCheckInstalled({
  45. pluginIds: [pluginId],
  46. enabled: !!pluginId,
  47. })
  48. const installedInfoPayload = installedInfo?.[pluginId]
  49. const installedVersion = installedInfoPayload?.installedVersion
  50. const hasInstalled = !!installedVersion
  51. const [isInstalling, setIsInstalling] = React.useState(false)
  52. const { mutateAsync: installPackageFromGitHub } = useInstallPackageFromGitHub()
  53. const { handleRefetch } = usePluginTaskList(payload.category)
  54. const { check } = checkTaskStatus()
  55. useEffect(() => {
  56. if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier)
  57. onInstalled()
  58. // eslint-disable-next-line react-hooks/exhaustive-deps
  59. }, [hasInstalled])
  60. const handleInstall = async () => {
  61. if (isInstalling) return
  62. setIsInstalling(true)
  63. onStartToInstall?.()
  64. try {
  65. const { owner, repo } = parseGitHubUrl(repoUrl)
  66. let taskId
  67. let isInstalled
  68. if (updatePayload) {
  69. const { all_installed, task_id } = await updateFromGitHub(
  70. `${owner}/${repo}`,
  71. selectedVersion,
  72. selectedPackage,
  73. updatePayload.originalPackageInfo.id,
  74. uniqueIdentifier,
  75. )
  76. taskId = task_id
  77. isInstalled = all_installed
  78. }
  79. else {
  80. if (hasInstalled) {
  81. const {
  82. all_installed,
  83. task_id,
  84. } = await updateFromGitHub(
  85. `${owner}/${repo}`,
  86. selectedVersion,
  87. selectedPackage,
  88. installedInfoPayload.uniqueIdentifier,
  89. uniqueIdentifier,
  90. )
  91. taskId = task_id
  92. isInstalled = all_installed
  93. }
  94. else {
  95. const { all_installed, task_id } = await installPackageFromGitHub({
  96. repoUrl: `${owner}/${repo}`,
  97. selectedVersion,
  98. selectedPackage,
  99. uniqueIdentifier,
  100. })
  101. taskId = task_id
  102. isInstalled = all_installed
  103. }
  104. }
  105. if (isInstalled) {
  106. onInstalled()
  107. return
  108. }
  109. handleRefetch()
  110. const { status, error } = await check({
  111. taskId,
  112. pluginUniqueIdentifier: uniqueIdentifier,
  113. })
  114. if (status === TaskStatus.failed) {
  115. onFailed(error)
  116. return
  117. }
  118. onInstalled(true)
  119. }
  120. catch (e) {
  121. if (typeof e === 'string') {
  122. onFailed(e)
  123. return
  124. }
  125. onFailed()
  126. }
  127. finally {
  128. setIsInstalling(false)
  129. }
  130. }
  131. return (
  132. <>
  133. <div className='system-md-regular text-text-secondary'>
  134. <p>{t(`${i18nPrefix}.readyToInstall`)}</p>
  135. </div>
  136. <div className='flex flex-wrap content-start items-start gap-1 self-stretch rounded-2xl bg-background-section-burn p-2'>
  137. <Card
  138. className='w-full'
  139. payload={pluginManifestToCardPluginProps(payload as PluginDeclaration)}
  140. titleLeft={!isLoading && <Version
  141. hasInstalled={hasInstalled}
  142. installedVersion={installedVersion}
  143. toInstallVersion={toInstallVersion}
  144. />}
  145. />
  146. </div>
  147. <div className='mt-4 flex items-center justify-end gap-2 self-stretch'>
  148. {!isInstalling && (
  149. <Button variant='secondary' className='min-w-[72px]' onClick={onBack}>
  150. {t('plugin.installModal.back')}
  151. </Button>
  152. )}
  153. <Button
  154. variant='primary'
  155. className='flex min-w-[72px] space-x-0.5'
  156. onClick={handleInstall}
  157. disabled={isInstalling || isLoading}
  158. >
  159. {isInstalling && <RiLoader2Line className='h-4 w-4 animate-spin-slow' />}
  160. <span>{t(`${i18nPrefix}.${isInstalling ? 'installing' : 'install'}`)}</span>
  161. </Button>
  162. </div>
  163. </>
  164. )
  165. }
  166. export default Loaded