feat: support install bundle from marketplace

This commit is contained in:
Joel 2024-11-25 18:01:03 +08:00
parent b93be49530
commit df049564e8
6 changed files with 79 additions and 32 deletions

View File

@ -22,8 +22,10 @@ const InstallByDSLList: FC<Props> = ({
onSelect, onSelect,
onLoadedAllPlugin, onLoadedAllPlugin,
}) => { }) => {
const { isLoading: isFetchingMarketplaceDataFromDSL, data: marketplaceFromDSLRes } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.plugin_unique_identifier!)) // DSL has id, to get plugin info to show more info
const { isLoading: isFetchingMarketplaceDataFromLocal, data: marketplaceResFromLocalRes } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value!)) const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.plugin_unique_identifier!))
// has meta(org,name,version), to get id
const { isLoading: isFetchingDataByMeta, data: infoByMeta, error: infoByMetaError } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value!))
const [plugins, doSetPlugins] = useState<(Plugin | undefined)[]>((() => { const [plugins, doSetPlugins] = useState<(Plugin | undefined)[]>((() => {
const hasLocalPackage = allPlugins.some(d => d.type === 'package') const hasLocalPackage = allPlugins.some(d => d.type === 'package')
if (!hasLocalPackage) if (!hasLocalPackage)
@ -75,8 +77,8 @@ const InstallByDSLList: FC<Props> = ({
}, [allPlugins]) }, [allPlugins])
useEffect(() => { useEffect(() => {
if (!isFetchingMarketplaceDataFromDSL && marketplaceFromDSLRes?.data.plugins) { if (!isFetchingMarketplaceDataById && infoGetById?.data.plugins) {
const payloads = marketplaceFromDSLRes?.data.plugins const payloads = infoGetById?.data.plugins
const failedIndex: number[] = [] const failedIndex: number[] = []
const nextPlugins = produce(pluginsRef.current, (draft) => { const nextPlugins = produce(pluginsRef.current, (draft) => {
marketPlaceInDSLIndex.forEach((index, i) => { marketPlaceInDSLIndex.forEach((index, i) => {
@ -92,11 +94,11 @@ const InstallByDSLList: FC<Props> = ({
setErrorIndexes([...errorIndexes, ...failedIndex]) setErrorIndexes([...errorIndexes, ...failedIndex])
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFetchingMarketplaceDataFromDSL]) }, [isFetchingMarketplaceDataById])
useEffect(() => { useEffect(() => {
if (!isFetchingMarketplaceDataFromLocal && marketplaceResFromLocalRes?.data.list) { if (!isFetchingDataByMeta && infoByMeta?.data.list) {
const payloads = marketplaceResFromLocalRes?.data.list const payloads = infoByMeta?.data.list
const failedIndex: number[] = [] const failedIndex: number[] = []
const nextPlugins = produce(pluginsRef.current, (draft) => { const nextPlugins = produce(pluginsRef.current, (draft) => {
marketPlaceInDSLIndex.forEach((index, i) => { marketPlaceInDSLIndex.forEach((index, i) => {
@ -117,7 +119,15 @@ const InstallByDSLList: FC<Props> = ({
setErrorIndexes([...errorIndexes, ...failedIndex]) setErrorIndexes([...errorIndexes, ...failedIndex])
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFetchingMarketplaceDataFromLocal]) }, [isFetchingDataByMeta])
useEffect(() => {
// get info all failed
if (infoByMetaError || infoByIdError)
setErrorIndexes([...errorIndexes, ...marketPlaceInDSLIndex])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [infoByMetaError, infoByIdError])
const isLoadedAllData = (plugins.filter(p => !!p).length + errorIndexes.length) === allPlugins.length const isLoadedAllData = (plugins.filter(p => !!p).length + errorIndexes.length) === allPlugins.length
useEffect(() => { useEffect(() => {

View File

@ -55,7 +55,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
updateModelProviders() updateModelProviders()
if (PluginType.tool.includes(manifest.category)) if (PluginType.tool.includes(manifest.category))
invalidateAllToolProviders() invalidateAllToolProviders()
}, [invalidateAllToolProviders, invalidateInstalledPluginList, manifest.category, updateModelProviders]) }, [invalidateAllToolProviders, invalidateInstalledPluginList, manifest, updateModelProviders])
const handleFailed = useCallback((errorMsg?: string) => { const handleFailed = useCallback((errorMsg?: string) => {
setStep(InstallStep.installFailed) setStep(InstallStep.installFailed)
@ -76,17 +76,6 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
{getTitle()} {getTitle()}
</div> </div>
</div> </div>
{
step === InstallStep.readyToInstall && (
<Install
uniqueIdentifier={uniqueIdentifier}
payload={manifest!}
onCancel={onClose}
onInstalled={handleInstalled}
onFailed={handleFailed}
/>
)
}
{ {
isBundle ? ( isBundle ? (
<ReadyToInstallBundle <ReadyToInstallBundle
@ -95,17 +84,32 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
onClose={onClose} onClose={onClose}
allPlugins={dependencies!} allPlugins={dependencies!}
/> />
) : ([InstallStep.installed, InstallStep.installFailed].includes(step)) && ( ) : (<>
<Installed {
payload={manifest!} step === InstallStep.readyToInstall && (
isMarketPayload <Install
isFailed={step === InstallStep.installFailed} uniqueIdentifier={uniqueIdentifier}
errMsg={errorMsg} payload={manifest!}
onCancel={onSuccess} onCancel={onClose}
/> onInstalled={handleInstalled}
onFailed={handleFailed}
/>
)}
{
[InstallStep.installed, InstallStep.installFailed].includes(step) && (
<Installed
payload={manifest!}
isMarketPayload
isFailed={step === InstallStep.installFailed}
errMsg={errorMsg}
onCancel={onSuccess}
/>
)
}
</>
) )
} }
</Modal> </Modal >
) )
} }

View File

@ -94,7 +94,7 @@ const Installed: FC<Props> = ({
</> </>
) )
}</>) }</>)
}, [payload.latest_version, supportCheckInstalled]) }, [payload.latest_version, payload.version, supportCheckInstalled])
return ( return (
<> <>

View File

@ -28,13 +28,15 @@ import {
useRouter, useRouter,
useSearchParams, useSearchParams,
} from 'next/navigation' } from 'next/navigation'
import type { Dependency } from '../types'
import type { PluginDeclaration, PluginManifestInMarket } from '../types' import type { PluginDeclaration, PluginManifestInMarket } from '../types'
import { sleep } from '@/utils' import { sleep } from '@/utils'
import { fetchManifestFromMarketPlace } from '@/service/plugins' import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
import { marketplaceApiPrefix } from '@/config' import { marketplaceApiPrefix } from '@/config'
import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
const PACKAGE_IDS_KEY = 'package-ids' const PACKAGE_IDS_KEY = 'package-ids'
const BUNDLE_INFO_KEY = 'bundle-info'
export type PluginPageProps = { export type PluginPageProps = {
plugins: React.ReactNode plugins: React.ReactNode
@ -58,6 +60,18 @@ const PluginPage = ({
return '' return ''
} }
}, [searchParams]) }, [searchParams])
const [dependencies, setDependencies] = useState<Dependency[]>([])
const bundleInfo = useMemo(() => {
const info = searchParams.get(BUNDLE_INFO_KEY)
try {
return info ? JSON.parse(info) : undefined
}
catch (e) {
return undefined
}
}, [searchParams])
const [isShowInstallFromMarketplace, { const [isShowInstallFromMarketplace, {
setTrue: showInstallFromMarketplace, setTrue: showInstallFromMarketplace,
setFalse: doHideInstallFromMarketplace, setFalse: doHideInstallFromMarketplace,
@ -67,6 +81,7 @@ const PluginPage = ({
doHideInstallFromMarketplace() doHideInstallFromMarketplace()
const url = new URL(window.location.href) const url = new URL(window.location.href)
url.searchParams.delete(PACKAGE_IDS_KEY) url.searchParams.delete(PACKAGE_IDS_KEY)
url.searchParams.delete(BUNDLE_INFO_KEY)
replace(url.toString()) replace(url.toString())
} }
const [manifest, setManifest] = useState<PluginDeclaration | PluginManifestInMarket | null>(null) const [manifest, setManifest] = useState<PluginDeclaration | PluginManifestInMarket | null>(null)
@ -83,10 +98,16 @@ const PluginPage = ({
icon: `${marketplaceApiPrefix}/plugins/${plugin.org}/${plugin.name}/icon`, icon: `${marketplaceApiPrefix}/plugins/${plugin.org}/${plugin.name}/icon`,
}) })
showInstallFromMarketplace() showInstallFromMarketplace()
return
}
if (bundleInfo) {
const { data } = await fetchBundleInfoFromMarketPlace(bundleInfo)
setDependencies(data.version.dependencies)
showInstallFromMarketplace()
} }
})() })()
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [packageId]) }, [packageId, bundleInfo])
const { const {
canManagement, canManagement,
@ -211,6 +232,8 @@ const PluginPage = ({
<InstallFromMarketplace <InstallFromMarketplace
manifest={manifest! as PluginManifestInMarket} manifest={manifest! as PluginManifestInMarket}
uniqueIdentifier={packageId} uniqueIdentifier={packageId}
isBundle={!!bundleInfo}
dependencies={dependencies}
onClose={hideInstallFromMarketplace} onClose={hideInstallFromMarketplace}
onSuccess={hideInstallFromMarketplace} onSuccess={hideInstallFromMarketplace}
/> />

View File

@ -1,6 +1,7 @@
import type { Fetcher } from 'swr' import type { Fetcher } from 'swr'
import { get, getMarketplace, post, upload } from './base' import { get, getMarketplace, post, upload } from './base'
import type { import type {
Dependency,
InstallPackageResponse, InstallPackageResponse,
Permissions, Permissions,
PluginDeclaration, PluginDeclaration,
@ -66,6 +67,14 @@ export const fetchManifestFromMarketPlace = async (uniqueIdentifier: string) =>
return getMarketplace<{ data: { plugin: PluginManifestInMarket, version: { version: string } } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`) return getMarketplace<{ data: { plugin: PluginManifestInMarket, version: { version: string } } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`)
} }
export const fetchBundleInfoFromMarketPlace = async ({
org,
name,
version,
}: Record<string, string>) => {
return getMarketplace<{ data: { version: { dependencies: Dependency[] } } }>(`/bundles/${org}/${name}/${version}`)
}
export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => { export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => {
return get<MarketplaceCollectionsResponse>(url) return get<MarketplaceCollectionsResponse>(url)
} }

View File

@ -110,6 +110,7 @@ export const useUploadGitHub = (payload: {
queryFn: () => post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', { queryFn: () => post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', {
body: payload, body: payload,
}), }),
retry: 0,
}) })
} }