merge
commit
ba53900ec4
@ -0,0 +1,25 @@
|
|||||||
|
export const tagKeys = [
|
||||||
|
'search',
|
||||||
|
'image',
|
||||||
|
'videos',
|
||||||
|
'weather',
|
||||||
|
'finance',
|
||||||
|
'design',
|
||||||
|
'travel',
|
||||||
|
'social',
|
||||||
|
'news',
|
||||||
|
'medical',
|
||||||
|
'productivity',
|
||||||
|
'education',
|
||||||
|
'business',
|
||||||
|
'entertainment',
|
||||||
|
'utilities',
|
||||||
|
'other',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const categoryKeys = [
|
||||||
|
'model',
|
||||||
|
'tool',
|
||||||
|
'extension',
|
||||||
|
'bundle',
|
||||||
|
]
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import { InstallStep } from '../../types'
|
||||||
|
import Install from './steps/install'
|
||||||
|
import Installed from './steps/installed'
|
||||||
|
import type { Dependency, InstallStatusResponse, Plugin } from '../../types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
step: InstallStep
|
||||||
|
onStepChange: (step: InstallStep) => void,
|
||||||
|
dependencies: Dependency[]
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadyToInstall: FC<Props> = ({
|
||||||
|
step,
|
||||||
|
onStepChange,
|
||||||
|
dependencies,
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const [installedPlugins, setInstalledPlugins] = useState<Plugin[]>([])
|
||||||
|
const [installStatus, setInstallStatus] = useState<InstallStatusResponse[]>([])
|
||||||
|
const handleInstalled = useCallback((plugins: Plugin[], installStatus: InstallStatusResponse[]) => {
|
||||||
|
setInstallStatus(installStatus)
|
||||||
|
setInstalledPlugins(plugins)
|
||||||
|
onStepChange(InstallStep.installed)
|
||||||
|
}, [onStepChange])
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{step === InstallStep.readyToInstall && (
|
||||||
|
<Install
|
||||||
|
fromDSLPayload={dependencies}
|
||||||
|
onCancel={onClose}
|
||||||
|
onInstalled={handleInstalled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{step === InstallStep.installed && (
|
||||||
|
<Installed
|
||||||
|
list={installedPlugins}
|
||||||
|
installStatus={installStatus}
|
||||||
|
onCancel={onClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(ReadyToInstall)
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useCallback } from 'react'
|
||||||
|
import type { PluginDeclaration } from '../../types'
|
||||||
|
import { InstallStep } from '../../types'
|
||||||
|
import Install from './steps/install'
|
||||||
|
import Installed from '../base/installed'
|
||||||
|
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
step: InstallStep
|
||||||
|
onStepChange: (step: InstallStep) => void,
|
||||||
|
onClose: () => void
|
||||||
|
uniqueIdentifier: string | null,
|
||||||
|
manifest: PluginDeclaration | null,
|
||||||
|
errorMsg: string | null,
|
||||||
|
onError: (errorMsg: string) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadyToInstall: FC<Props> = ({
|
||||||
|
step,
|
||||||
|
onStepChange,
|
||||||
|
onClose,
|
||||||
|
uniqueIdentifier,
|
||||||
|
manifest,
|
||||||
|
errorMsg,
|
||||||
|
onError,
|
||||||
|
}) => {
|
||||||
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
|
|
||||||
|
const handleInstalled = useCallback(() => {
|
||||||
|
invalidateInstalledPluginList()
|
||||||
|
onStepChange(InstallStep.installed)
|
||||||
|
}, [invalidateInstalledPluginList, onStepChange])
|
||||||
|
|
||||||
|
const handleFailed = useCallback((errorMsg?: string) => {
|
||||||
|
onStepChange(InstallStep.installFailed)
|
||||||
|
if (errorMsg)
|
||||||
|
onError(errorMsg)
|
||||||
|
}, [onError, onStepChange])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
step === InstallStep.readyToInstall && (
|
||||||
|
<Install
|
||||||
|
uniqueIdentifier={uniqueIdentifier!}
|
||||||
|
payload={manifest!}
|
||||||
|
onCancel={onClose}
|
||||||
|
onInstalled={handleInstalled}
|
||||||
|
onFailed={handleFailed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
([InstallStep.uploadFailed, InstallStep.installed, InstallStep.installFailed].includes(step)) && (
|
||||||
|
<Installed
|
||||||
|
payload={manifest}
|
||||||
|
isFailed={[InstallStep.uploadFailed, InstallStep.installFailed].includes(step)}
|
||||||
|
errMsg={errorMsg}
|
||||||
|
onCancel={onClose}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(ReadyToInstall)
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useCallback } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import type {
|
||||||
|
OffsetOptions,
|
||||||
|
Placement,
|
||||||
|
} from '@floating-ui/react'
|
||||||
|
import { useVersionListOfPlugin } from '@/service/use-plugins'
|
||||||
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
disabled?: boolean
|
||||||
|
isShow: boolean
|
||||||
|
onShowChange: (isShow: boolean) => void
|
||||||
|
pluginID: string
|
||||||
|
currentVersion: string
|
||||||
|
trigger: React.ReactNode
|
||||||
|
placement?: Placement
|
||||||
|
offset?: OffsetOptions
|
||||||
|
onSelect: ({
|
||||||
|
version,
|
||||||
|
unique_identifier,
|
||||||
|
}: {
|
||||||
|
version: string
|
||||||
|
unique_identifier: string
|
||||||
|
}) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PluginVersionPicker: FC<Props> = ({
|
||||||
|
disabled = false,
|
||||||
|
isShow,
|
||||||
|
onShowChange,
|
||||||
|
pluginID,
|
||||||
|
currentVersion,
|
||||||
|
trigger,
|
||||||
|
placement = 'bottom-start',
|
||||||
|
offset = {
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: -16,
|
||||||
|
},
|
||||||
|
onSelect,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const format = t('appLog.dateTimeFormat').split(' ')[0]
|
||||||
|
const { formatDate } = useTimestamp()
|
||||||
|
|
||||||
|
const handleTriggerClick = () => {
|
||||||
|
if (disabled) return
|
||||||
|
onShowChange(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: res } = useVersionListOfPlugin(pluginID)
|
||||||
|
|
||||||
|
const handleSelect = useCallback(({ version, unique_identifier }: {
|
||||||
|
version: string
|
||||||
|
unique_identifier: string
|
||||||
|
}) => {
|
||||||
|
if (currentVersion === version)
|
||||||
|
return
|
||||||
|
onSelect({ version, unique_identifier })
|
||||||
|
onShowChange(false)
|
||||||
|
}, [currentVersion, onSelect])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PortalToFollowElem
|
||||||
|
placement={placement}
|
||||||
|
offset={offset}
|
||||||
|
open={isShow}
|
||||||
|
onOpenChange={onShowChange}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger
|
||||||
|
className={cn('inline-flex items-center cursor-pointer', disabled && 'cursor-default')}
|
||||||
|
onClick={handleTriggerClick}
|
||||||
|
>
|
||||||
|
{trigger}
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
|
||||||
|
<PortalToFollowElemContent className='z-[1000]'>
|
||||||
|
<div className="relative w-[209px] p-1 rounded-xl bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg">
|
||||||
|
<div className='px-3 pt-1 pb-0.5 text-text-tertiary system-xs-medium-uppercase'>
|
||||||
|
{t('plugin.detailPanel.switchVersion')}
|
||||||
|
</div>
|
||||||
|
<div className='relative'>
|
||||||
|
{res?.data.versions.map(version => (
|
||||||
|
<div
|
||||||
|
key={version.unique_identifier}
|
||||||
|
className={cn(
|
||||||
|
'h-7 px-3 py-1 flex items-center gap-1 rounded-lg hover:bg-state-base-hover cursor-pointer',
|
||||||
|
currentVersion === version.version && 'opacity-30 cursor-default hover:bg-transparent',
|
||||||
|
)}
|
||||||
|
onClick={() => handleSelect({
|
||||||
|
version: version.version,
|
||||||
|
unique_identifier: version.unique_identifier,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className='grow flex items-center'>
|
||||||
|
<div className='text-text-secondary system-sm-medium'>{version.version}</div>
|
||||||
|
{currentVersion === version.version && <Badge className='ml-1' text='CURRENT'/>}
|
||||||
|
</div>
|
||||||
|
<div className='shrink-0 text-text-tertiary system-xs-regular'>{formatDate(version.created_at, format)}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(PluginVersionPicker)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
categoryKeys,
|
||||||
|
tagKeys,
|
||||||
|
} from './constants'
|
||||||
|
|
||||||
|
export const getValidTagKeys = (tags: string[]) => {
|
||||||
|
return tags.filter(tag => tagKeys.includes(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidCategoryKeys = (category?: string) => {
|
||||||
|
const currentCategory = categoryKeys.find(key => key === category)
|
||||||
|
return currentCategory ? `${currentCategory}s` : ''
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { type NextRequest, NextResponse } from 'next/server'
|
||||||
|
import { Octokit } from '@octokit/core'
|
||||||
|
import { RequestError } from '@octokit/request-error'
|
||||||
|
import { GITHUB_ACCESS_TOKEN } from '@/config'
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: GITHUB_ACCESS_TOKEN,
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: NextRequest,
|
||||||
|
{ params }: { params: Promise<Params> },
|
||||||
|
) {
|
||||||
|
const { owner, repo } = (await params)
|
||||||
|
try {
|
||||||
|
const releasesRes = await octokit.request('GET /repos/{owner}/{repo}/releases', {
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
headers: {
|
||||||
|
'X-GitHub-Api-Version': '2022-11-28',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return NextResponse.json(releasesRes)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error instanceof RequestError)
|
||||||
|
return NextResponse.json(error.response)
|
||||||
|
else
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue