feat: plugin task use query

This commit is contained in:
StyleZhang 2024-11-12 17:58:14 +08:00
parent 9c6aafd415
commit 13d3f67746
9 changed files with 111 additions and 74 deletions

View File

@ -9,7 +9,7 @@ import { pluginManifestToCardPluginProps } from '../../utils'
import { useTranslation } from 'react-i18next'
import { installPackageFromGitHub, uninstallPlugin } from '@/service/plugins'
import { RiLoader2Line } from '@remixicon/react'
import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/plugin-tasks/store'
import { usePluginTaskList } from '@/service/use-plugins'
import checkTaskStatus from '../../base/check-task-status'
import { parseGitHubUrl } from '../../utils'
@ -40,7 +40,7 @@ const Loaded: React.FC<LoadedProps> = ({
}) => {
const { t } = useTranslation()
const [isInstalling, setIsInstalling] = React.useState(false)
const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling)
const { handleRefetch } = usePluginTaskList()
const { check } = checkTaskStatus()
const handleInstall = async () => {
@ -64,7 +64,7 @@ const Loaded: React.FC<LoadedProps> = ({
return
}
setPluginTasksWithPolling()
handleRefetch()
await check({
taskId,
pluginUniqueIdentifier: uniqueIdentifier,

View File

@ -10,7 +10,7 @@ import { RiLoader2Line } from '@remixicon/react'
import Badge, { BadgeState } from '@/app/components/base/badge/index'
import { useInstallPackageFromLocal } from '@/service/use-plugins'
import checkTaskStatus from '../../base/check-task-status'
import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/plugin-tasks/store'
import { usePluginTaskList } from '@/service/use-plugins'
const i18nPrefix = 'plugin.installModal'
@ -45,7 +45,7 @@ const Installed: FC<Props> = ({
onCancel()
}
const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling)
const { handleRefetch } = usePluginTaskList()
const handleInstall = async () => {
if (isInstalling) return
setIsInstalling(true)
@ -60,7 +60,7 @@ const Installed: FC<Props> = ({
onInstalled()
return
}
setPluginTasksWithPolling()
handleRefetch()
await check({
taskId,
pluginUniqueIdentifier: uniqueIdentifier,

View File

@ -16,7 +16,6 @@ import InstallPluginDropdown from './install-plugin-dropdown'
import { useUploader } from './use-uploader'
import usePermission from './use-permission'
import DebugInfo from './debug-info'
import { usePluginTasksStore } from './plugin-tasks/store'
import PluginTasks from './plugin-tasks'
import Button from '@/app/components/base/button'
import TabSlider from '@/app/components/base/tab-slider'
@ -111,12 +110,6 @@ const PluginPage = ({
const { dragging, fileUploader, fileChangeHandle, removeFile } = uploaderProps
const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling)
useEffect(() => {
setPluginTasksWithPolling()
}, [setPluginTasksWithPolling])
return (
<div
id='marketplace-container'

View File

@ -1,10 +1,22 @@
import { usePluginTasksStore } from './store'
import { useCallback } from 'react'
import { TaskStatus } from '@/app/components/plugins/types'
import type { PluginStatus } from '@/app/components/plugins/types'
import {
useMutationClearTaskPlugin,
usePluginTaskList,
} from '@/service/use-plugins'
export const usePluginTaskStatus = () => {
const pluginTasks = usePluginTasksStore(s => s.pluginTasks)
const allPlugins = pluginTasks.map(task => task.plugins).flat()
const {
pluginTasks,
} = usePluginTaskList()
const { mutate } = useMutationClearTaskPlugin()
const allPlugins = pluginTasks.map(task => task.plugins.map((plugin) => {
return {
...plugin,
taskId: task.id,
}
})).flat()
const errorPlugins: PluginStatus[] = []
const successPlugins: PluginStatus[] = []
const runningPlugins: PluginStatus[] = []
@ -18,10 +30,18 @@ export const usePluginTaskStatus = () => {
successPlugins.push(plugin)
})
const handleClearErrorPlugin = useCallback((taskId: string, pluginId: string) => {
mutate({
taskId,
pluginId,
})
}, [mutate])
return {
errorPlugins,
successPlugins,
runningPlugins,
totalPluginsLength: allPlugins.length,
handleClearErrorPlugin,
}
}

View File

@ -17,16 +17,20 @@ import {
import Tooltip from '@/app/components/base/tooltip'
import Button from '@/app/components/base/button'
import ProgressCircle from '@/app/components/base/progress-bar/progress-circle'
import CardIcon from '@/app/components/plugins/card/base/card-icon'
import cn from '@/utils/classnames'
import { useGetLanguage } from '@/context/i18n'
const PluginTasks = () => {
const { t } = useTranslation()
const language = useGetLanguage()
const [open, setOpen] = useState(false)
const {
errorPlugins,
runningPlugins,
successPlugins,
totalPluginsLength,
handleClearErrorPlugin,
} = usePluginTaskStatus()
const isInstalling = runningPlugins.length > 0 && errorPlugins.length === 0 && successPlugins.length === 0
@ -113,20 +117,31 @@ const PluginTasks = () => {
<PortalToFollowElemContent className='z-10'>
<div className='p-1 pb-2 w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
<div className='flex items-center px-2 pt-1 h-7 system-sm-semibold-uppercase'>{t('plugin.task.installedError')}</div>
<div className='flex items-center p-1 pl-2 h-8 rounded-lg hover:bg-state-base-hover'>
<div className='relative flex items-center justify-center mr-2 w-6 h-6 rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiErrorWarningFill className='absolute -right-0.5 -bottom-0.5 w-3 h-3 text-text-destructive' />
</div>
<div className='grow system-md-regular text-text-secondary truncate'>
DuckDuckGo Search
</div>
<Button
size='small'
variant='ghost-accent'
>
{t('common.operation.clear')}
</Button>
</div>
{
errorPlugins.map(errorPlugin => (
<div
key={errorPlugin.plugin_unique_identifier}
className='flex items-center p-1 pl-2 h-8 rounded-lg hover:bg-state-base-hover'
>
<div className='relative flex items-center justify-center mr-2 w-6 h-6 rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiErrorWarningFill className='absolute -right-0.5 -bottom-0.5 w-3 h-3 text-text-destructive' />
<CardIcon
src={errorPlugin.icon}
/>
</div>
<div className='grow system-md-regular text-text-secondary truncate'>
{errorPlugin.labels[language]}
</div>
<Button
size='small'
variant='ghost-accent'
onClick={() => handleClearErrorPlugin(errorPlugin.taskId, errorPlugin.plugin_id)}
>
{t('common.operation.clear')}
</Button>
</div>
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>

View File

@ -1,40 +0,0 @@
import { create } from 'zustand'
import type { PluginTask } from '@/app/components/plugins/types'
import { fetchPluginTasks } from '@/service/plugins'
type PluginTasksStore = {
pluginTasks: PluginTask[]
setPluginTasks: (tasks: PluginTask[]) => void
setPluginTasksWithPolling: () => void
}
let pluginTasksTimer: NodeJS.Timeout | null = null
export const usePluginTasksStore = create<PluginTasksStore>(set => ({
pluginTasks: [],
setPluginTasks: (tasks: PluginTask[]) => set({ pluginTasks: tasks }),
setPluginTasksWithPolling: async () => {
if (pluginTasksTimer) {
clearTimeout(pluginTasksTimer)
pluginTasksTimer = null
}
const handleUpdatePluginTasks = async () => {
const { tasks } = await fetchPluginTasks()
set({ pluginTasks: tasks })
if (tasks.length && !tasks.every(task => task.status === 'success')) {
pluginTasksTimer = setTimeout(() => {
handleUpdatePluginTasks()
}, 5000)
}
else {
if (pluginTasksTimer) {
clearTimeout(pluginTasksTimer)
pluginTasksTimer = null
}
}
}
handleUpdatePluginTasks()
},
}))

View File

@ -260,6 +260,9 @@ export type PluginStatus = {
plugin_id: string
status: TaskStatus
message: string
icon: string
labels: Record<Locale, string>
taskId: string
}
export type PluginTask = {

View File

@ -12,7 +12,7 @@ import { pluginManifestToCardPluginProps } from '@/app/components/plugins/instal
import useGetIcon from '../install-plugin/base/use-get-icon'
import { updateFromMarketPlace } from '@/service/plugins'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/plugin-tasks/store'
import { usePluginTaskList } from '@/service/use-plugins'
const i18nPrefix = 'plugin.upgrade'
@ -56,7 +56,7 @@ const UpdatePluginModal: FC<Props> = ({
}
const [uploadStep, setUploadStep] = useState<UploadStep>(UploadStep.notStarted)
const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling)
const { handleRefetch } = usePluginTaskList()
const configBtnText = useMemo(() => {
return ({
@ -82,7 +82,7 @@ const UpdatePluginModal: FC<Props> = ({
onSave()
return
}
setPluginTasksWithPolling()
handleRefetch()
await check({
taskId,
pluginUniqueIdentifier: targetPackageInfo.id,
@ -98,7 +98,7 @@ const UpdatePluginModal: FC<Props> = ({
onSave()
onCancel()
}
}, [onCancel, onSave, uploadStep, check, originalPackageInfo.id, setPluginTasksWithPolling, targetPackageInfo.id])
}, [onCancel, onSave, uploadStep, check, originalPackageInfo.id, handleRefetch, targetPackageInfo.id])
const usedInAppInfo = useMemo(() => {
return (
<div className='flex px-0.5 justify-center items-center gap-0.5'>

View File

@ -1,8 +1,10 @@
import { useCallback, useState } from 'react'
import type {
DebugInfo as DebugInfoTypes,
InstallPackageResponse,
InstalledPluginListResponse,
Permissions,
PluginTask,
PluginsFromMarketplaceResponse,
} from '@/app/components/plugins/types'
import type {
@ -115,3 +117,47 @@ export const useMutationPluginsFromMarketplace = () => {
},
})
}
const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList']
export const usePluginTaskList = () => {
const [enabled, setEnabled] = useState(true)
const {
data,
isFetched,
refetch,
...rest
} = useQuery({
queryKey: usePluginTaskListKey,
queryFn: async () => {
const currentData = await get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100')
const taskDone = currentData.tasks.every(task => task.total_plugins === task.completed_plugins)
if (taskDone)
setEnabled(false)
return currentData
},
refetchInterval: 5000,
enabled,
})
const handleRefetch = useCallback(() => {
setEnabled(true)
refetch()
}, [refetch])
return {
data,
pluginTasks: data?.tasks || [],
isFetched,
handleRefetch,
...rest,
}
}
export const useMutationClearTaskPlugin = () => {
return useMutation({
mutationFn: ({ taskId, pluginId }: { taskId: string; pluginId: string }) => {
return post<{ success: boolean }>(`/workspaces/current/plugin/task/${taskId}/delete/${pluginId}`)
},
})
}