您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

install-multi.tsx 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  4. import type { Dependency, GitHubItemAndMarketPlaceDependency, PackageDependency, Plugin, VersionInfo } from '../../../types'
  5. import MarketplaceItem from '../item/marketplace-item'
  6. import GithubItem from '../item/github-item'
  7. import { useFetchPluginsInMarketPlaceByIds, useFetchPluginsInMarketPlaceByInfo } from '@/service/use-plugins'
  8. import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
  9. import produce from 'immer'
  10. import PackageItem from '../item/package-item'
  11. import LoadingError from '../../base/loading-error'
  12. type Props = {
  13. allPlugins: Dependency[]
  14. selectedPlugins: Plugin[]
  15. onSelect: (plugin: Plugin, selectedIndex: number) => void
  16. onLoadedAllPlugin: (installedInfo: Record<string, VersionInfo>) => void
  17. isFromMarketPlace?: boolean
  18. }
  19. const InstallByDSLList: FC<Props> = ({
  20. allPlugins,
  21. selectedPlugins,
  22. onSelect,
  23. onLoadedAllPlugin,
  24. isFromMarketPlace,
  25. }) => {
  26. // DSL has id, to get plugin info to show more info
  27. const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.marketplace_plugin_unique_identifier!))
  28. // has meta(org,name,version), to get id
  29. const { isLoading: isFetchingDataByMeta, data: infoByMeta, error: infoByMetaError } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value!))
  30. const [plugins, doSetPlugins] = useState<(Plugin | undefined)[]>((() => {
  31. const hasLocalPackage = allPlugins.some(d => d.type === 'package')
  32. if (!hasLocalPackage)
  33. return []
  34. const _plugins = allPlugins.map((d) => {
  35. if (d.type === 'package') {
  36. return {
  37. ...(d as any).value.manifest,
  38. plugin_id: (d as any).value.unique_identifier,
  39. }
  40. }
  41. return undefined
  42. })
  43. return _plugins
  44. })())
  45. const pluginsRef = React.useRef<(Plugin | undefined)[]>(plugins)
  46. const setPlugins = useCallback((p: (Plugin | undefined)[]) => {
  47. doSetPlugins(p)
  48. pluginsRef.current = p
  49. }, [])
  50. const [errorIndexes, setErrorIndexes] = useState<number[]>([])
  51. const handleGitHubPluginFetched = useCallback((index: number) => {
  52. return (p: Plugin) => {
  53. const nextPlugins = produce(pluginsRef.current, (draft) => {
  54. draft[index] = p
  55. })
  56. setPlugins(nextPlugins)
  57. }
  58. }, [setPlugins])
  59. const handleGitHubPluginFetchError = useCallback((index: number) => {
  60. return () => {
  61. setErrorIndexes([...errorIndexes, index])
  62. }
  63. }, [errorIndexes])
  64. const marketPlaceInDSLIndex = useMemo(() => {
  65. const res: number[] = []
  66. allPlugins.forEach((d, index) => {
  67. if (d.type === 'marketplace')
  68. res.push(index)
  69. })
  70. return res
  71. }, [allPlugins])
  72. useEffect(() => {
  73. if (!isFetchingMarketplaceDataById && infoGetById?.data.plugins) {
  74. const sortedList = allPlugins.filter(d => d.type === 'marketplace').map((d) => {
  75. const p = d as GitHubItemAndMarketPlaceDependency
  76. const id = p.value.marketplace_plugin_unique_identifier?.split(':')[0]
  77. return infoGetById.data.plugins.find(item => item.plugin_id === id)!
  78. })
  79. const payloads = sortedList
  80. const failedIndex: number[] = []
  81. const nextPlugins = produce(pluginsRef.current, (draft) => {
  82. marketPlaceInDSLIndex.forEach((index, i) => {
  83. if (payloads[i]) {
  84. draft[index] = {
  85. ...payloads[i],
  86. version: payloads[i].version || payloads[i].latest_version,
  87. }
  88. }
  89. else { failedIndex.push(index) }
  90. })
  91. })
  92. setPlugins(nextPlugins)
  93. if (failedIndex.length > 0)
  94. setErrorIndexes([...errorIndexes, ...failedIndex])
  95. }
  96. // eslint-disable-next-line react-hooks/exhaustive-deps
  97. }, [isFetchingMarketplaceDataById])
  98. useEffect(() => {
  99. if (!isFetchingDataByMeta && infoByMeta?.data.list) {
  100. const payloads = infoByMeta?.data.list
  101. const failedIndex: number[] = []
  102. const nextPlugins = produce(pluginsRef.current, (draft) => {
  103. marketPlaceInDSLIndex.forEach((index, i) => {
  104. if (payloads[i]) {
  105. const item = payloads[i]
  106. draft[index] = {
  107. ...item.plugin,
  108. plugin_id: item.version.unique_identifier,
  109. }
  110. }
  111. else {
  112. failedIndex.push(index)
  113. }
  114. })
  115. })
  116. setPlugins(nextPlugins)
  117. if (failedIndex.length > 0)
  118. setErrorIndexes([...errorIndexes, ...failedIndex])
  119. }
  120. // eslint-disable-next-line react-hooks/exhaustive-deps
  121. }, [isFetchingDataByMeta])
  122. useEffect(() => {
  123. // get info all failed
  124. if (infoByMetaError || infoByIdError)
  125. setErrorIndexes([...errorIndexes, ...marketPlaceInDSLIndex])
  126. // eslint-disable-next-line react-hooks/exhaustive-deps
  127. }, [infoByMetaError, infoByIdError])
  128. const isLoadedAllData = (plugins.filter(p => !!p).length + errorIndexes.length) === allPlugins.length
  129. const { installedInfo } = useCheckInstalled({
  130. pluginIds: plugins?.filter(p => !!p).map((d) => {
  131. return `${d?.org || d?.author}/${d?.name}`
  132. }) || [],
  133. enabled: isLoadedAllData,
  134. })
  135. const getVersionInfo = useCallback((pluginId: string) => {
  136. const pluginDetail = installedInfo?.[pluginId]
  137. const hasInstalled = !!pluginDetail
  138. return {
  139. hasInstalled,
  140. installedVersion: pluginDetail?.installedVersion,
  141. toInstallVersion: '',
  142. }
  143. }, [installedInfo])
  144. useEffect(() => {
  145. if (isLoadedAllData && installedInfo)
  146. onLoadedAllPlugin(installedInfo!)
  147. // eslint-disable-next-line react-hooks/exhaustive-deps
  148. }, [isLoadedAllData, installedInfo])
  149. const handleSelect = useCallback((index: number) => {
  150. return () => {
  151. onSelect(plugins[index]!, index)
  152. }
  153. }, [onSelect, plugins])
  154. return (
  155. <>
  156. {allPlugins.map((d, index) => {
  157. if (errorIndexes.includes(index)) {
  158. return (
  159. <LoadingError key={index} />
  160. )
  161. }
  162. const plugin = plugins[index]
  163. if (d.type === 'github') {
  164. return (<GithubItem
  165. key={index}
  166. checked={!!selectedPlugins.find(p => p.plugin_id === plugins[index]?.plugin_id)}
  167. onCheckedChange={handleSelect(index)}
  168. dependency={d as GitHubItemAndMarketPlaceDependency}
  169. onFetchedPayload={handleGitHubPluginFetched(index)}
  170. onFetchError={handleGitHubPluginFetchError(index)}
  171. versionInfo={getVersionInfo(`${plugin?.org || plugin?.author}/${plugin?.name}`)}
  172. />)
  173. }
  174. if (d.type === 'marketplace') {
  175. return (
  176. <MarketplaceItem
  177. key={index}
  178. checked={!!selectedPlugins.find(p => p.plugin_id === plugins[index]?.plugin_id)}
  179. onCheckedChange={handleSelect(index)}
  180. payload={plugin}
  181. version={(d as GitHubItemAndMarketPlaceDependency).value.version! || plugin?.version || ''}
  182. versionInfo={getVersionInfo(`${plugin?.org || plugin?.author}/${plugin?.name}`)}
  183. />
  184. )
  185. }
  186. // Local package
  187. return (
  188. <PackageItem
  189. key={index}
  190. checked={!!selectedPlugins.find(p => p.plugin_id === plugins[index]?.plugin_id)}
  191. onCheckedChange={handleSelect(index)}
  192. payload={d as PackageDependency}
  193. isFromMarketPlace={isFromMarketPlace}
  194. versionInfo={getVersionInfo(`${plugin?.org || plugin?.author}/${plugin?.name}`)}
  195. />
  196. )
  197. })
  198. }
  199. </>
  200. )
  201. }
  202. export default React.memo(InstallByDSLList)