import React, { useCallback, useEffect, useState } from "react"; import Translate, { translate } from "@docusaurus/Translate"; import { usePagination } from "react-use-pagination"; import Admonition from "@theme/Admonition"; import PluginForm from "@/components/Form/Plugin"; import Modal from "@/components/Modal"; import Paginate from "@/components/Paginate"; import ResourceCard from "@/components/Resource/Card"; import ResourceDetailCard from "@/components/Resource/DetailCard"; import Searcher from "@/components/Searcher"; import StoreToolbar, { type Action, type Sorter, } from "@/components/Store/Toolbar"; import { authorFilter, tagFilter } from "@/libs/filter"; import { useSearchControl } from "@/libs/search"; import { SortMode } from "@/libs/sorter"; import { fetchRegistryData, loadFailedTitle } from "@/libs/store"; import { useToolbar } from "@/libs/toolbar"; import type { Plugin } from "@/types/plugin"; export default function PluginPage(): React.ReactNode { const [plugins, setPlugins] = useState(null); const pluginCount = plugins?.length ?? 0; const loading = plugins === null; const [error, setError] = useState(null); const [isOpenModal, setIsOpenModal] = useState(false); const [isOpenCardModal, setIsOpenCardModal] = useState(false); const [clickedPlugin, setClickedPlugin] = useState(null); const [sortMode, setSortMode] = useState(SortMode.Default); const sorterTool: Sorter = { label: sortMode === SortMode.Default ? translate({ id: "pages.store.sorter.label.default", description: "The label of default sorter", message: "默认顺序", }) : translate({ id: "pages.store.sorter.label.updateDesc", description: "The label of updateDesc sorter", message: "更新时间倒序", }), icon: ["fas", "sort-amount-down"], active: sortMode === SortMode.UpdateDesc, onClick: () => { setSortMode( sortMode === SortMode.Default ? SortMode.UpdateDesc : SortMode.Default ); }, }; const getSortedPlugins = (plugins: Plugin[]): Plugin[] => { if (sortMode === SortMode.UpdateDesc) { return [...plugins].sort( (a, b) => new Date(b.time).getTime() - new Date(a.time).getTime() ); } return plugins; }; const { filteredResources: filteredPlugins, searcherTags, addFilter, onSearchQueryChange, onSearchQuerySubmit, onSearchBackspace, onSearchClear, onSearchTagClick, } = useSearchControl(getSortedPlugins(plugins ?? [])); const filteredPluginCount = filteredPlugins.length; const { startIndex, endIndex, totalPages, currentPage, setNextPage, setPreviousPage, setPage, previousEnabled, nextEnabled, } = usePagination({ totalItems: filteredPlugins.length, initialPageSize: 12, }); const currentPlugins = filteredPlugins.slice(startIndex, endIndex + 1); // load plugins asynchronously useEffect(() => { fetchRegistryData("plugin") .then(setPlugins) .catch((e) => { setError(e); console.error(e); }); }, []); const { filters: filterTools } = useToolbar({ resources: plugins ?? [], addFilter, }); const actionTool: Action = { label: "发布插件", icon: ["fas", "plus"], onClick: () => { setIsOpenModal(true); }, }; const onCardClick = useCallback((plugin: Plugin) => { setClickedPlugin(plugin); setIsOpenCardModal(true); }, []); const onCardTagClick = useCallback( (tag: string) => { addFilter(tagFilter(tag)); }, [addFilter] ); const onCardAuthorClick = useCallback( (author: string) => { addFilter(authorFilter(author)); }, [addFilter] ); return ( <>

{pluginCount === filteredPluginCount ? ( {"当前共有 {pluginCount} 个插件"} ) : ( {"当前共有 {filteredPluginCount} / {pluginCount} 个插件"} )}

{error ? ( {error.message} ) : loading ? (

) : (
{currentPlugins.map((plugin, index) => ( onCardClick(plugin)} onTagClick={onCardTagClick} onAuthorClick={() => onCardAuthorClick(plugin.author)} /> ))}
)} {isOpenModal && ( )} {isOpenCardModal && ( {clickedPlugin && } )} ); }