feat: handle from market install

This commit is contained in:
Joel 2024-10-29 16:33:27 +08:00
parent 0886d7bb8b
commit 0dcbb34cab
8 changed files with 65 additions and 23 deletions

View File

@ -41,7 +41,7 @@ const Card = ({
const { type, name, org, label, brief, icon, verified } = payload const { type, name, org, label, brief, icon, verified } = payload
const getLocalizedText = (obj: Record<string, string> | undefined) => const getLocalizedText = (obj: Record<string, string> | undefined) =>
obj?.[locale] || obj?.['en-US'] || '' obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
const wrapClassName = cn('relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className) const wrapClassName = cn('relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className)
if (isLoading) { if (isLoading) {

View File

@ -1,15 +1,16 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import type { PluginDeclaration } from '../../types' import type { PluginDeclaration, PluginManifestInMarket } from '../../types'
import Card from '../../card' import Card from '../../card'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { pluginManifestToCardPluginProps } from '../utils' import { pluginManifestInMarketToPluginProps, pluginManifestToCardPluginProps } from '../utils'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Badge, { BadgeState } from '@/app/components/base/badge/index' import Badge, { BadgeState } from '@/app/components/base/badge/index'
type Props = { type Props = {
payload?: PluginDeclaration | null payload?: PluginDeclaration | PluginManifestInMarket | null
isMarketPayload?: boolean
isFailed: boolean isFailed: boolean
errMsg?: string | null errMsg?: string | null
onCancel: () => void onCancel: () => void
@ -17,6 +18,7 @@ type Props = {
const Installed: FC<Props> = ({ const Installed: FC<Props> = ({
payload, payload,
isMarketPayload,
isFailed, isFailed,
errMsg, errMsg,
onCancel, onCancel,
@ -30,10 +32,10 @@ const Installed: FC<Props> = ({
<div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'> <div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'>
<Card <Card
className='w-full' className='w-full'
payload={pluginManifestToCardPluginProps(payload)} payload={isMarketPayload ? pluginManifestInMarketToPluginProps(payload as PluginManifestInMarket) : pluginManifestToCardPluginProps(payload as PluginDeclaration)}
installed={!isFailed} installed={!isFailed}
installFailed={isFailed} installFailed={isFailed}
titleLeft={<Badge className='mx-1' size="s" state={BadgeState.Default}>{payload.version}</Badge>} titleLeft={<Badge className='mx-1' size="s" state={BadgeState.Default}>{(payload as PluginDeclaration).version || (payload as PluginManifestInMarket).latest_version}</Badge>}
/> />
</div> </div>
)} )}

View File

@ -2,7 +2,7 @@
import React, { useCallback, useState } from 'react' import React, { useCallback, useState } from 'react'
import Modal from '@/app/components/base/modal' import Modal from '@/app/components/base/modal'
import type { PluginDeclaration } from '../../types' import type { PluginManifestInMarket } from '../../types'
import { InstallStep } from '../../types' import { InstallStep } from '../../types'
import Install from './steps/install' import Install from './steps/install'
import Installed from '../base/installed' import Installed from '../base/installed'
@ -10,9 +10,9 @@ import { useTranslation } from 'react-i18next'
const i18nPrefix = 'plugin.installModal' const i18nPrefix = 'plugin.installModal'
interface InstallFromMarketplaceProps { type InstallFromMarketplaceProps = {
uniqueIdentifier: string uniqueIdentifier: string
manifest: PluginDeclaration manifest: PluginManifestInMarket
onSuccess: () => void onSuccess: () => void
onClose: () => void onClose: () => void
} }
@ -75,6 +75,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
([InstallStep.installed, InstallStep.installFailed].includes(step)) && ( ([InstallStep.installed, InstallStep.installFailed].includes(step)) && (
<Installed <Installed
payload={manifest!} payload={manifest!}
isMarketPayload
isFailed={step === InstallStep.installFailed} isFailed={step === InstallStep.installFailed}
errMsg={errorMsg} errMsg={errorMsg}
onCancel={onSuccess} onCancel={onSuccess}

View File

@ -2,9 +2,9 @@
import type { FC } from 'react' import type { FC } from 'react'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { RiInformation2Line } from '@remixicon/react' import { RiInformation2Line } from '@remixicon/react'
import type { PluginDeclaration } from '../../../types' import type { PluginManifestInMarket } from '../../../types'
import Card from '../../../card' import Card from '../../../card'
import { pluginManifestToCardPluginProps } from '../../utils' import { pluginManifestInMarketToPluginProps } from '../../utils'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiLoader2Line } from '@remixicon/react' import { RiLoader2Line } from '@remixicon/react'
@ -14,9 +14,9 @@ import checkTaskStatus from '../../base/check-task-status'
const i18nPrefix = 'plugin.installModal' const i18nPrefix = 'plugin.installModal'
interface Props { type Props = {
uniqueIdentifier: string uniqueIdentifier: string
payload: PluginDeclaration payload: PluginManifestInMarket
onCancel: () => void onCancel: () => void
onInstalled: () => void onInstalled: () => void
onFailed: (message?: string) => void onFailed: (message?: string) => void
@ -74,14 +74,14 @@ const Installed: FC<Props> = ({
const versionInfo = useMemo(() => { const versionInfo = useMemo(() => {
return (<>{ return (<>{
payload.version === toInstallVersion || !supportCheckInstalled payload.latest_version === toInstallVersion || !supportCheckInstalled
? ( ? (
<Badge className='mx-1' size="s" state={BadgeState.Default}>{payload.version}</Badge> <Badge className='mx-1' size="s" state={BadgeState.Default}>{payload.latest_version}</Badge>
) )
: ( : (
<> <>
<Badge className='mx-1' size="s" state={BadgeState.Warning}> <Badge className='mx-1' size="s" state={BadgeState.Warning}>
{`${payload.version} -> ${toInstallVersion}`} {`${payload.latest_version} -> ${toInstallVersion}`}
</Badge> </Badge>
<div className='flex px-0.5 justify-center items-center gap-0.5'> <div className='flex px-0.5 justify-center items-center gap-0.5'>
<div className='text-text-warning system-xs-medium'>Used in 3 apps</div> <div className='text-text-warning system-xs-medium'>Used in 3 apps</div>
@ -101,7 +101,7 @@ const Installed: FC<Props> = ({
<div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'> <div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'>
<Card <Card
className='w-full' className='w-full'
payload={pluginManifestToCardPluginProps(payload)} payload={pluginManifestInMarketToPluginProps(payload)}
titleLeft={versionInfo} titleLeft={versionInfo}
/> />
</div> </div>

View File

@ -1,4 +1,4 @@
import type { Plugin, PluginDeclaration } from '../types' import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../types'
export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaration): Plugin => { export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaration): Plugin => {
return { return {
@ -20,3 +20,24 @@ export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaratio
}, },
} }
} }
export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManifestInMarket): Plugin => {
return {
type: pluginManifest.category,
category: pluginManifest.category,
name: pluginManifest.name,
version: pluginManifest.latest_version,
latest_version: pluginManifest.latest_version,
org: pluginManifest.org,
label: pluginManifest.label,
brief: pluginManifest.brief,
icon: pluginManifest.icon,
verified: pluginManifest.verified,
introduction: pluginManifest.introduction,
repository: '',
install_count: 0,
endpoint: {
settings: [],
},
}
}

View File

@ -29,9 +29,10 @@ import {
useRouter, useRouter,
useSearchParams, useSearchParams,
} from 'next/navigation' } from 'next/navigation'
import type { PluginDeclaration } from '../types' import type { PluginDeclaration, PluginManifestInMarket } from '../types'
import { sleep } from '@/utils' import { sleep } from '@/utils'
import { fetchManifestFromMarketPlace } from '@/service/plugins' import { fetchManifestFromMarketPlace } from '@/service/plugins'
import { marketplaceApiPrefix } from '@/config'
const PACKAGE_IDS_KEY = 'package-ids' const PACKAGE_IDS_KEY = 'package-ids'
@ -68,14 +69,18 @@ const PluginPage = ({
url.searchParams.delete(PACKAGE_IDS_KEY) url.searchParams.delete(PACKAGE_IDS_KEY)
replace(url.toString()) replace(url.toString())
} }
const [manifest, setManifest] = useState<PluginDeclaration | null>(null) const [manifest, setManifest] = useState<PluginDeclaration | PluginManifestInMarket | null>(null)
useEffect(() => { useEffect(() => {
(async () => { (async () => {
await sleep(100) await sleep(100)
if (packageId) { if (packageId) {
const { data } = await fetchManifestFromMarketPlace(encodeURIComponent(packageId)) const { data } = await fetchManifestFromMarketPlace(encodeURIComponent(packageId))
setManifest(data.plugin) const { plugin } = data
setManifest({
...plugin,
icon: `${marketplaceApiPrefix}/plugins/${plugin.org}/${plugin.name}/icon`,
})
showInstallFromMarketplace() showInstallFromMarketplace()
} }
})() })()
@ -229,7 +234,7 @@ const PluginPage = ({
{ {
isShowInstallFromMarketplace && ( isShowInstallFromMarketplace && (
<InstallFromMarketplace <InstallFromMarketplace
manifest={manifest!} manifest={manifest! as PluginManifestInMarket}
uniqueIdentifier={packageId} uniqueIdentifier={packageId}
onClose={hideInstallFromMarketplace} onClose={hideInstallFromMarketplace}
onSuccess={hideInstallFromMarketplace} onSuccess={hideInstallFromMarketplace}

View File

@ -70,6 +70,18 @@ export type PluginDeclaration = {
model: any // TODO model: any // TODO
} }
export type PluginManifestInMarket = {
name: string
org: string
icon: string
label: Record<Locale, string>
category: PluginType
latest_version: string
brief: Record<Locale, string>
introduction: string
verified: boolean
}
export type PluginDetail = { export type PluginDetail = {
id: string id: string
created_at: string created_at: string

View File

@ -7,6 +7,7 @@ import type {
EndpointsResponse, EndpointsResponse,
InstallPackageResponse, InstallPackageResponse,
PluginDeclaration, PluginDeclaration,
PluginManifestInMarket,
TaskStatusResponse, TaskStatusResponse,
UpdateEndpointRequest, UpdateEndpointRequest,
} from '@/app/components/plugins/types' } from '@/app/components/plugins/types'
@ -80,7 +81,7 @@ export const fetchManifest = async (uniqueIdentifier: string) => {
} }
export const fetchManifestFromMarketPlace = async (uniqueIdentifier: string) => { export const fetchManifestFromMarketPlace = async (uniqueIdentifier: string) => {
return getMarketplace<{ data: { plugin: PluginDeclaration } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`) return getMarketplace<{ data: { plugin: PluginManifestInMarket } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`)
} }
export const installPackageFromMarketPlace = async (uniqueIdentifier: string) => { export const installPackageFromMarketPlace = async (uniqueIdentifier: string) => {