From 3f8a10613d2cde0c9d81cc40e1f14a2f33a7ae7b Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 7 Nov 2024 16:52:22 +0800 Subject: [PATCH] refactor: remove unused fetchInstalledPluginList function and integrate useInstalledPluginList hook --- web/app/components/base/tab-slider/index.tsx | 8 ++--- .../install-from-local-package/index.tsx | 8 ++--- .../components/plugins/plugin-item/action.tsx | 6 ++-- .../components/plugins/plugin-item/index.tsx | 5 ++-- .../plugins/plugin-page/context.tsx | 12 -------- .../plugins/plugin-page/empty/index.tsx | 14 +++++---- .../plugin-page/install-plugin-dropdown.tsx | 6 ++-- .../plugins/plugin-page/plugins-panel.tsx | 15 +++++----- web/service/plugins.ts | 5 ---- web/service/use-plugins.ts | 29 +++++++++++++++++++ 10 files changed, 62 insertions(+), 46 deletions(-) create mode 100644 web/service/use-plugins.ts diff --git a/web/app/components/base/tab-slider/index.tsx b/web/app/components/base/tab-slider/index.tsx index 5e290d1dc9..1b4e42e0d7 100644 --- a/web/app/components/base/tab-slider/index.tsx +++ b/web/app/components/base/tab-slider/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import { useEffect, useState } from 'react' import cn from '@/utils/classnames' import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { usePluginPageContext } from '../../plugins/plugin-page/context' +import { useInstalledPluginList } from '@/service/use-plugins' type Option = { value: string text: string @@ -23,7 +23,7 @@ const TabSlider: FC = ({ }) => { const [activeIndex, setActiveIndex] = useState(options.findIndex(option => option.value === value)) const [sliderStyle, setSliderStyle] = useState({}) - const pluginList = usePluginPageContext(v => v.installedPluginList) + const { data: pluginList } = useInstalledPluginList() const updateSliderStyle = (index: number) => { const tabElement = document.getElementById(`tab-${index}`) @@ -69,13 +69,13 @@ const TabSlider: FC = ({ {option.text} {/* if no plugin installed, the badge won't show */} {option.value === 'plugins' - && pluginList.length > 0 + && (pluginList?.plugins.length ?? 0) > 0 && - {pluginList.length} + {pluginList?.plugins.length} } diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx index d53be9e49b..86f31c36f2 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx @@ -9,7 +9,7 @@ import Install from './steps/install' import Installed from '../base/installed' import { useTranslation } from 'react-i18next' 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' @@ -29,7 +29,7 @@ const InstallFromLocalPackage: React.FC = ({ const [uniqueIdentifier, setUniqueIdentifier] = useState(null) const [manifest, setManifest] = useState(null) const [errorMsg, setErrorMsg] = useState(null) - const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const getTitle = useCallback(() => { if (step === InstallStep.uploadFailed) @@ -67,9 +67,9 @@ const InstallFromLocalPackage: React.FC = ({ }, []) const handleInstalled = useCallback(() => { - mutateInstalledPluginList() + invalidateInstalledPluginList() setStep(InstallStep.installed) - }, [mutateInstalledPluginList]) + }, [invalidateInstalledPluginList]) const handleFailed = useCallback((errorMsg?: string) => { setStep(InstallStep.installFailed) diff --git a/web/app/components/plugins/plugin-item/action.tsx b/web/app/components/plugins/plugin-item/action.tsx index e838654203..9ff38a508b 100644 --- a/web/app/components/plugins/plugin-item/action.tsx +++ b/web/app/components/plugins/plugin-item/action.tsx @@ -14,7 +14,7 @@ import { useGitHubReleases } from '../install-plugin/hooks' import { compareVersion, getLatestVersion } from '@/utils/semver' import Toast from '@/app/components/base/toast' import { useModalContext } from '@/context/modal-context' -import { usePluginPageContext } from '../plugin-page/context' +import { useInvalidateInstalledPluginList } from '@/service/use-plugins' const i18nPrefix = 'plugin.action' @@ -52,7 +52,7 @@ const Action: FC = ({ }] = useBoolean(false) const { fetchReleases } = useGitHubReleases() const { setShowUpdatePluginModal } = useModalContext() - const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const handleFetchNewVersion = async () => { try { @@ -64,7 +64,7 @@ const Action: FC = ({ if (compareVersion(latestVersion, version) === 1) { setShowUpdatePluginModal({ onSaveCallback: () => { - mutateInstalledPluginList() + invalidateInstalledPluginList() }, payload: { type: PluginSource.github, diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index b66a819911..5a6a3a6ca2 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -21,6 +21,7 @@ import Action from './action' import cn from '@/utils/classnames' import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import { useLanguage } from '../../header/account-setting/model-provider-page/hooks' +import { useInvalidateInstalledPluginList } from '@/service/use-plugins' type Props = { className?: string @@ -35,7 +36,7 @@ const PluginItem: FC = ({ const { t } = useTranslation() const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail) const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) - const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const { source, @@ -93,7 +94,7 @@ const PluginItem: FC = ({ isShowDelete meta={meta} onDelete={() => { - mutateInstalledPluginList() + invalidateInstalledPluginList() }} /> diff --git a/web/app/components/plugins/plugin-page/context.tsx b/web/app/components/plugins/plugin-page/context.tsx index cbe8f3bf93..697d28a022 100644 --- a/web/app/components/plugins/plugin-page/context.tsx +++ b/web/app/components/plugins/plugin-page/context.tsx @@ -14,8 +14,6 @@ import { useSelector as useAppContextSelector } from '@/context/app-context' import type { Permissions, PluginDetail } from '../types' import type { FilterState } from './filter-management' import { PermissionType } from '../types' -import { fetchInstalledPluginList } from '@/service/plugins' -import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' @@ -25,9 +23,6 @@ export type PluginPageContextValue = { setPermissions: (permissions: PluginPageContextValue['permissions']) => void currentPluginDetail: PluginDetail | undefined setCurrentPluginDetail: (plugin: PluginDetail) => void - installedPluginList: PluginDetail[] - mutateInstalledPluginList: () => void - isPluginListLoading: boolean filters: FilterState setFilters: (filter: FilterState) => void activeTab: string @@ -44,9 +39,6 @@ export const PluginPageContext = createContext({ setPermissions: () => {}, currentPluginDetail: undefined, setCurrentPluginDetail: () => {}, - installedPluginList: [], - mutateInstalledPluginList: () => {}, - isPluginListLoading: true, filters: { categories: [], tags: [], @@ -80,7 +72,6 @@ export const PluginPageContextProvider = ({ tags: [], searchQuery: '', }) - const { data, mutate: mutateInstalledPluginList, isLoading: isPluginListLoading } = useSWR({ url: '/workspaces/current/plugin/list' }, fetchInstalledPluginList) const [currentPluginDetail, setCurrentPluginDetail] = useState() const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) @@ -106,9 +97,6 @@ export const PluginPageContextProvider = ({ setPermissions, currentPluginDetail, setCurrentPluginDetail, - installedPluginList: data?.plugins || [], - mutateInstalledPluginList, - isPluginListLoading, filters, setFilters, activeTab, diff --git a/web/app/components/plugins/plugin-page/empty/index.tsx b/web/app/components/plugins/plugin-page/empty/index.tsx index 74d88fd004..3092e0f444 100644 --- a/web/app/components/plugins/plugin-page/empty/index.tsx +++ b/web/app/components/plugins/plugin-page/empty/index.tsx @@ -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 InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import { usePluginPageContext } from '../context' -import type { PluginDetail } from '../../types' import { Group } from '@/app/components/base/icons/src/vender/other' import { useSelector as useAppContextSelector } from '@/context/app-context' import Line from '../../marketplace/empty/line' +import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' const Empty = () => { const fileInputRef = useRef(null) @@ -25,14 +25,15 @@ const Empty = () => { } } const filters = usePluginPageContext(v => v.filters) - const pluginList = usePluginPageContext(v => v.installedPluginList) as PluginDetail[] + const { data: pluginList } = useInstalledPluginList() + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const text = useMemo(() => { - if (pluginList.length === 0) + if (pluginList?.plugins.length === 0) return 'No plugins installed' if (filters.categories.length > 0 || filters.tags.length > 0 || filters.searchQuery) return 'No plugins found' - }, [pluginList.length, filters]) + }, [pluginList, filters]) return (
@@ -95,7 +96,10 @@ const Empty = () => {
- {selectedAction === 'github' && setSelectedAction(null)} />} + {selectedAction === 'github' && { invalidateInstalledPluginList() }} + onClose={() => setSelectedAction(null)} + />} {selectedAction === 'local' && selectedFile && ( void @@ -28,7 +28,7 @@ const InstallPluginDropdown = ({ const [selectedAction, setSelectedAction] = useState(null) const [selectedFile, setSelectedFile] = useState(null) const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) - const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0] @@ -117,7 +117,7 @@ const InstallPluginDropdown = ({ {selectedAction === 'github' && { mutateInstalledPluginList() }} + onSuccess={() => { invalidateInstalledPluginList() }} onClose={() => setSelectedAction(null)} />} {selectedAction === 'local' && selectedFile diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index ae108d5b65..466df72066 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -1,9 +1,9 @@ 'use client' import { useMemo } from 'react' -import type { PluginDetail } from '../types' import type { FilterState } from './filter-management' import FilterManagement from './filter-management' import List from './list' +import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import { usePluginPageContext } from './context' import { useDebounceFn } from 'ahooks' @@ -12,9 +12,8 @@ import Loading from '../../base/loading' const PluginsPanel = () => { const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters]) as [FilterState, (filter: FilterState) => void] - const pluginList = usePluginPageContext(v => v.installedPluginList) as PluginDetail[] - const isPluginListLoading = usePluginPageContext(v => v.isPluginListLoading) - const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) + const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList() + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => { setFilters(filters) @@ -22,7 +21,7 @@ const PluginsPanel = () => { const filteredList = useMemo(() => { const { categories, searchQuery, tags } = filters - const filteredList = pluginList.filter((plugin) => { + const filteredList = pluginList?.plugins.filter((plugin) => { return ( (categories.length === 0 || categories.includes(plugin.declaration.category)) && (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag))) @@ -40,16 +39,16 @@ const PluginsPanel = () => { onFilterChange={handleFilterChange} /> - {isPluginListLoading ? : filteredList.length > 0 ? ( + {isPluginListLoading ? : (filteredList?.length ?? 0) > 0 ? (
- +
) : ( )} - mutateInstalledPluginList()}/> + invalidateInstalledPluginList()}/> ) } diff --git a/web/service/plugins.ts b/web/service/plugins.ts index 8e18f7f0b9..e9de724256 100644 --- a/web/service/plugins.ts +++ b/web/service/plugins.ts @@ -6,7 +6,6 @@ import type { EndpointsRequest, EndpointsResponse, InstallPackageResponse, - InstalledPluginListResponse, Permissions, PluginDeclaration, PluginManifestInMarket, @@ -140,10 +139,6 @@ export const updatePermission = async (permissions: Permissions) => { return post('/workspaces/current/plugin/permission/change', { body: permissions }) } -export const fetchInstalledPluginList: Fetcher = ({ url }) => { - return get(url) -} - export const uninstallPlugin = async (pluginId: string) => { return post('/workspaces/current/plugin/uninstall', { body: { plugin_installation_id: pluginId } }) } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts new file mode 100644 index 0000000000..69e79c5bab --- /dev/null +++ b/web/service/use-plugins.ts @@ -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({ + queryKey: useInstalledPluginListKey, + queryFn: () => get('/workspaces/current/plugin/list'), + }) +} + +export const useInvalidateInstalledPluginList = () => { + const queryClient = useQueryClient() + return () => { + queryClient.invalidateQueries( + { + queryKey: useInstalledPluginListKey, + }) + } +}