plugins-panel.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. 'use client'
  2. import { useMemo } from 'react'
  3. import type { FilterState } from './filter-management'
  4. import FilterManagement from './filter-management'
  5. import List from './list'
  6. import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
  7. import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
  8. import { usePluginPageContext } from './context'
  9. import { useDebounceFn } from 'ahooks'
  10. import Empty from './empty'
  11. import Loading from '../../base/loading'
  12. const PluginsPanel = () => {
  13. const filters = usePluginPageContext(v => v.filters) as FilterState
  14. const setFilters = usePluginPageContext(v => v.setFilters)
  15. const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList()
  16. const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
  17. const currentPluginID = usePluginPageContext(v => v.currentPluginID)
  18. const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
  19. const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => {
  20. setFilters(filters)
  21. }, { wait: 500 })
  22. const filteredList = useMemo(() => {
  23. const { categories, searchQuery, tags } = filters
  24. const filteredList = pluginList?.plugins.filter((plugin) => {
  25. return (
  26. (categories.length === 0 || categories.includes(plugin.declaration.category))
  27. && (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
  28. && (searchQuery === '' || plugin.plugin_id.toLowerCase().includes(searchQuery.toLowerCase()))
  29. )
  30. })
  31. return filteredList
  32. }, [pluginList, filters])
  33. const currentPluginDetail = useMemo(() => {
  34. const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentPluginID)
  35. return detail
  36. }, [currentPluginID, pluginList?.plugins])
  37. const handleHide = () => setCurrentPluginID(undefined)
  38. return (
  39. <>
  40. <div className='flex flex-col pt-1 pb-3 px-12 justify-center items-start gap-3 self-stretch'>
  41. <div className='h-px self-stretch bg-divider-subtle'></div>
  42. <FilterManagement
  43. onFilterChange={handleFilterChange}
  44. />
  45. </div>
  46. {isPluginListLoading ? <Loading type='app' /> : (filteredList?.length ?? 0) > 0 ? (
  47. <div className='flex px-12 items-start content-start gap-2 grow self-stretch flex-wrap'>
  48. <div className='w-full'>
  49. <List pluginList={filteredList || []} />
  50. </div>
  51. </div>
  52. ) : (
  53. <Empty />
  54. )}
  55. <PluginDetailPanel
  56. detail={currentPluginDetail}
  57. onUpdate={() => invalidateInstalledPluginList()}
  58. onHide={handleHide}
  59. />
  60. </>
  61. )
  62. }
  63. export default PluginsPanel