refactor: remove unused fetchInstalledPluginList function and integrate useInstalledPluginList hook

This commit is contained in:
twwu 2024-11-07 16:52:22 +08:00
parent c7f8a0fc7b
commit 3f8a10613d
10 changed files with 62 additions and 46 deletions

View File

@ -2,7 +2,7 @@ import type { FC } from 'react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import Badge, { BadgeState } from '@/app/components/base/badge/index' import Badge, { BadgeState } from '@/app/components/base/badge/index'
import { usePluginPageContext } from '../../plugins/plugin-page/context' import { useInstalledPluginList } from '@/service/use-plugins'
type Option = { type Option = {
value: string value: string
text: string text: string
@ -23,7 +23,7 @@ const TabSlider: FC<TabSliderProps> = ({
}) => { }) => {
const [activeIndex, setActiveIndex] = useState(options.findIndex(option => option.value === value)) const [activeIndex, setActiveIndex] = useState(options.findIndex(option => option.value === value))
const [sliderStyle, setSliderStyle] = useState({}) const [sliderStyle, setSliderStyle] = useState({})
const pluginList = usePluginPageContext(v => v.installedPluginList) const { data: pluginList } = useInstalledPluginList()
const updateSliderStyle = (index: number) => { const updateSliderStyle = (index: number) => {
const tabElement = document.getElementById(`tab-${index}`) const tabElement = document.getElementById(`tab-${index}`)
@ -69,13 +69,13 @@ const TabSlider: FC<TabSliderProps> = ({
{option.text} {option.text}
{/* if no plugin installed, the badge won't show */} {/* if no plugin installed, the badge won't show */}
{option.value === 'plugins' {option.value === 'plugins'
&& pluginList.length > 0 && (pluginList?.plugins.length ?? 0) > 0
&& <Badge && <Badge
size='s' size='s'
uppercase={true} uppercase={true}
state={BadgeState.Default} state={BadgeState.Default}
> >
{pluginList.length} {pluginList?.plugins.length}
</Badge> </Badge>
} }
</div> </div>

View File

@ -9,7 +9,7 @@ import Install from './steps/install'
import Installed from '../base/installed' import Installed from '../base/installed'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import { usePluginPageContext } from '../../plugin-page/context' import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
const i18nPrefix = 'plugin.installModal' const i18nPrefix = 'plugin.installModal'
@ -29,7 +29,7 @@ const InstallFromLocalPackage: React.FC<InstallFromLocalPackageProps> = ({
const [uniqueIdentifier, setUniqueIdentifier] = useState<string | null>(null) const [uniqueIdentifier, setUniqueIdentifier] = useState<string | null>(null)
const [manifest, setManifest] = useState<PluginDeclaration | null>(null) const [manifest, setManifest] = useState<PluginDeclaration | null>(null)
const [errorMsg, setErrorMsg] = useState<string | null>(null) const [errorMsg, setErrorMsg] = useState<string | null>(null)
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const getTitle = useCallback(() => { const getTitle = useCallback(() => {
if (step === InstallStep.uploadFailed) if (step === InstallStep.uploadFailed)
@ -67,9 +67,9 @@ const InstallFromLocalPackage: React.FC<InstallFromLocalPackageProps> = ({
}, []) }, [])
const handleInstalled = useCallback(() => { const handleInstalled = useCallback(() => {
mutateInstalledPluginList() invalidateInstalledPluginList()
setStep(InstallStep.installed) setStep(InstallStep.installed)
}, [mutateInstalledPluginList]) }, [invalidateInstalledPluginList])
const handleFailed = useCallback((errorMsg?: string) => { const handleFailed = useCallback((errorMsg?: string) => {
setStep(InstallStep.installFailed) setStep(InstallStep.installFailed)

View File

@ -14,7 +14,7 @@ import { useGitHubReleases } from '../install-plugin/hooks'
import { compareVersion, getLatestVersion } from '@/utils/semver' import { compareVersion, getLatestVersion } from '@/utils/semver'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import { useModalContext } from '@/context/modal-context' import { useModalContext } from '@/context/modal-context'
import { usePluginPageContext } from '../plugin-page/context' import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
const i18nPrefix = 'plugin.action' const i18nPrefix = 'plugin.action'
@ -52,7 +52,7 @@ const Action: FC<Props> = ({
}] = useBoolean(false) }] = useBoolean(false)
const { fetchReleases } = useGitHubReleases() const { fetchReleases } = useGitHubReleases()
const { setShowUpdatePluginModal } = useModalContext() const { setShowUpdatePluginModal } = useModalContext()
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const handleFetchNewVersion = async () => { const handleFetchNewVersion = async () => {
try { try {
@ -64,7 +64,7 @@ const Action: FC<Props> = ({
if (compareVersion(latestVersion, version) === 1) { if (compareVersion(latestVersion, version) === 1) {
setShowUpdatePluginModal({ setShowUpdatePluginModal({
onSaveCallback: () => { onSaveCallback: () => {
mutateInstalledPluginList() invalidateInstalledPluginList()
}, },
payload: { payload: {
type: PluginSource.github, type: PluginSource.github,

View File

@ -21,6 +21,7 @@ import Action from './action'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
import { useLanguage } from '../../header/account-setting/model-provider-page/hooks' import { useLanguage } from '../../header/account-setting/model-provider-page/hooks'
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
type Props = { type Props = {
className?: string className?: string
@ -35,7 +36,7 @@ const PluginItem: FC<Props> = ({
const { t } = useTranslation() const { t } = useTranslation()
const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail) const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail)
const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail)
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const { const {
source, source,
@ -93,7 +94,7 @@ const PluginItem: FC<Props> = ({
isShowDelete isShowDelete
meta={meta} meta={meta}
onDelete={() => { onDelete={() => {
mutateInstalledPluginList() invalidateInstalledPluginList()
}} }}
/> />
</div> </div>

View File

@ -14,8 +14,6 @@ import { useSelector as useAppContextSelector } from '@/context/app-context'
import type { Permissions, PluginDetail } from '../types' import type { Permissions, PluginDetail } from '../types'
import type { FilterState } from './filter-management' import type { FilterState } from './filter-management'
import { PermissionType } from '../types' import { PermissionType } from '../types'
import { fetchInstalledPluginList } from '@/service/plugins'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
@ -25,9 +23,6 @@ export type PluginPageContextValue = {
setPermissions: (permissions: PluginPageContextValue['permissions']) => void setPermissions: (permissions: PluginPageContextValue['permissions']) => void
currentPluginDetail: PluginDetail | undefined currentPluginDetail: PluginDetail | undefined
setCurrentPluginDetail: (plugin: PluginDetail) => void setCurrentPluginDetail: (plugin: PluginDetail) => void
installedPluginList: PluginDetail[]
mutateInstalledPluginList: () => void
isPluginListLoading: boolean
filters: FilterState filters: FilterState
setFilters: (filter: FilterState) => void setFilters: (filter: FilterState) => void
activeTab: string activeTab: string
@ -44,9 +39,6 @@ export const PluginPageContext = createContext<PluginPageContextValue>({
setPermissions: () => {}, setPermissions: () => {},
currentPluginDetail: undefined, currentPluginDetail: undefined,
setCurrentPluginDetail: () => {}, setCurrentPluginDetail: () => {},
installedPluginList: [],
mutateInstalledPluginList: () => {},
isPluginListLoading: true,
filters: { filters: {
categories: [], categories: [],
tags: [], tags: [],
@ -80,7 +72,6 @@ export const PluginPageContextProvider = ({
tags: [], tags: [],
searchQuery: '', searchQuery: '',
}) })
const { data, mutate: mutateInstalledPluginList, isLoading: isPluginListLoading } = useSWR({ url: '/workspaces/current/plugin/list' }, fetchInstalledPluginList)
const [currentPluginDetail, setCurrentPluginDetail] = useState<PluginDetail | undefined>() const [currentPluginDetail, setCurrentPluginDetail] = useState<PluginDetail | undefined>()
const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures)
@ -106,9 +97,6 @@ export const PluginPageContextProvider = ({
setPermissions, setPermissions,
currentPluginDetail, currentPluginDetail,
setCurrentPluginDetail, setCurrentPluginDetail,
installedPluginList: data?.plugins || [],
mutateInstalledPluginList,
isPluginListLoading,
filters, filters,
setFilters, setFilters,
activeTab, activeTab,

View File

@ -5,10 +5,10 @@ import { Github } from '@/app/components/base/icons/src/vender/solid/general'
import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github' import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github'
import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package'
import { usePluginPageContext } from '../context' import { usePluginPageContext } from '../context'
import type { PluginDetail } from '../../types'
import { Group } from '@/app/components/base/icons/src/vender/other' import { Group } from '@/app/components/base/icons/src/vender/other'
import { useSelector as useAppContextSelector } from '@/context/app-context' import { useSelector as useAppContextSelector } from '@/context/app-context'
import Line from '../../marketplace/empty/line' import Line from '../../marketplace/empty/line'
import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
const Empty = () => { const Empty = () => {
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
@ -25,14 +25,15 @@ const Empty = () => {
} }
} }
const filters = usePluginPageContext(v => v.filters) const filters = usePluginPageContext(v => v.filters)
const pluginList = usePluginPageContext(v => v.installedPluginList) as PluginDetail[] const { data: pluginList } = useInstalledPluginList()
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const text = useMemo(() => { const text = useMemo(() => {
if (pluginList.length === 0) if (pluginList?.plugins.length === 0)
return 'No plugins installed' return 'No plugins installed'
if (filters.categories.length > 0 || filters.tags.length > 0 || filters.searchQuery) if (filters.categories.length > 0 || filters.tags.length > 0 || filters.searchQuery)
return 'No plugins found' return 'No plugins found'
}, [pluginList.length, filters]) }, [pluginList, filters])
return ( return (
<div className='grow w-full relative z-0'> <div className='grow w-full relative z-0'>
@ -95,7 +96,10 @@ const Empty = () => {
</div> </div>
</div> </div>
</div> </div>
{selectedAction === 'github' && <InstallFromGitHub onClose={() => setSelectedAction(null)} />} {selectedAction === 'github' && <InstallFromGitHub
onSuccess={() => { invalidateInstalledPluginList() }}
onClose={() => setSelectedAction(null)}
/>}
{selectedAction === 'local' && selectedFile {selectedAction === 'local' && selectedFile
&& (<InstallFromLocalPackage && (<InstallFromLocalPackage
file={selectedFile} file={selectedFile}

View File

@ -15,7 +15,7 @@ import {
PortalToFollowElemTrigger, PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem' } from '@/app/components/base/portal-to-follow-elem'
import { useSelector as useAppContextSelector } from '@/context/app-context' import { useSelector as useAppContextSelector } from '@/context/app-context'
import { usePluginPageContext } from './context' import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
type Props = { type Props = {
onSwitchToMarketplaceTab: () => void onSwitchToMarketplaceTab: () => void
@ -28,7 +28,7 @@ const InstallPluginDropdown = ({
const [selectedAction, setSelectedAction] = useState<string | null>(null) const [selectedAction, setSelectedAction] = useState<string | null>(null)
const [selectedFile, setSelectedFile] = useState<File | null>(null) const [selectedFile, setSelectedFile] = useState<File | null>(null)
const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures)
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0] const file = event.target.files?.[0]
@ -117,7 +117,7 @@ const InstallPluginDropdown = ({
</PortalToFollowElemContent> </PortalToFollowElemContent>
</div> </div>
{selectedAction === 'github' && <InstallFromGitHub {selectedAction === 'github' && <InstallFromGitHub
onSuccess={() => { mutateInstalledPluginList() }} onSuccess={() => { invalidateInstalledPluginList() }}
onClose={() => setSelectedAction(null)} onClose={() => setSelectedAction(null)}
/>} />}
{selectedAction === 'local' && selectedFile {selectedAction === 'local' && selectedFile

View File

@ -1,9 +1,9 @@
'use client' 'use client'
import { useMemo } from 'react' import { useMemo } from 'react'
import type { PluginDetail } from '../types'
import type { FilterState } from './filter-management' import type { FilterState } from './filter-management'
import FilterManagement from './filter-management' import FilterManagement from './filter-management'
import List from './list' import List from './list'
import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
import { usePluginPageContext } from './context' import { usePluginPageContext } from './context'
import { useDebounceFn } from 'ahooks' import { useDebounceFn } from 'ahooks'
@ -12,9 +12,8 @@ import Loading from '../../base/loading'
const PluginsPanel = () => { const PluginsPanel = () => {
const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters]) as [FilterState, (filter: FilterState) => void] const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters]) as [FilterState, (filter: FilterState) => void]
const pluginList = usePluginPageContext(v => v.installedPluginList) as PluginDetail[] const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList()
const isPluginListLoading = usePluginPageContext(v => v.isPluginListLoading) const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList)
const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => { const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => {
setFilters(filters) setFilters(filters)
@ -22,7 +21,7 @@ const PluginsPanel = () => {
const filteredList = useMemo(() => { const filteredList = useMemo(() => {
const { categories, searchQuery, tags } = filters const { categories, searchQuery, tags } = filters
const filteredList = pluginList.filter((plugin) => { const filteredList = pluginList?.plugins.filter((plugin) => {
return ( return (
(categories.length === 0 || categories.includes(plugin.declaration.category)) (categories.length === 0 || categories.includes(plugin.declaration.category))
&& (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag))) && (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
@ -40,16 +39,16 @@ const PluginsPanel = () => {
onFilterChange={handleFilterChange} onFilterChange={handleFilterChange}
/> />
</div> </div>
{isPluginListLoading ? <Loading type='app' /> : filteredList.length > 0 ? ( {isPluginListLoading ? <Loading type='app' /> : (filteredList?.length ?? 0) > 0 ? (
<div className='flex px-12 items-start content-start gap-2 flex-grow self-stretch flex-wrap'> <div className='flex px-12 items-start content-start gap-2 flex-grow self-stretch flex-wrap'>
<div className='w-full'> <div className='w-full'>
<List pluginList={filteredList} /> <List pluginList={filteredList || []} />
</div> </div>
</div> </div>
) : ( ) : (
<Empty /> <Empty />
)} )}
<PluginDetailPanel onDelete={() => mutateInstalledPluginList()}/> <PluginDetailPanel onDelete={() => invalidateInstalledPluginList()}/>
</> </>
) )
} }

View File

@ -6,7 +6,6 @@ import type {
EndpointsRequest, EndpointsRequest,
EndpointsResponse, EndpointsResponse,
InstallPackageResponse, InstallPackageResponse,
InstalledPluginListResponse,
Permissions, Permissions,
PluginDeclaration, PluginDeclaration,
PluginManifestInMarket, PluginManifestInMarket,
@ -140,10 +139,6 @@ export const updatePermission = async (permissions: Permissions) => {
return post('/workspaces/current/plugin/permission/change', { body: permissions }) return post('/workspaces/current/plugin/permission/change', { body: permissions })
} }
export const fetchInstalledPluginList: Fetcher<InstalledPluginListResponse, { url: string }> = ({ url }) => {
return get<InstalledPluginListResponse>(url)
}
export const uninstallPlugin = async (pluginId: string) => { export const uninstallPlugin = async (pluginId: string) => {
return post<UninstallPluginResponse>('/workspaces/current/plugin/uninstall', { body: { plugin_installation_id: pluginId } }) return post<UninstallPluginResponse>('/workspaces/current/plugin/uninstall', { body: { plugin_installation_id: pluginId } })
} }

View File

@ -0,0 +1,29 @@
import type { InstalledPluginListResponse } from '@/app/components/plugins/types'
import { get } from './base'
import {
useQueryClient,
} from '@tanstack/react-query'
import {
useQuery,
} from '@tanstack/react-query'
const NAME_SPACE = 'plugins'
const useInstalledPluginListKey = [NAME_SPACE, 'installedPluginList']
export const useInstalledPluginList = () => {
return useQuery<InstalledPluginListResponse>({
queryKey: useInstalledPluginListKey,
queryFn: () => get<InstalledPluginListResponse>('/workspaces/current/plugin/list'),
})
}
export const useInvalidateInstalledPluginList = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries(
{
queryKey: useInstalledPluginListKey,
})
}
}