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