import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { MarketServer, ApiResponse, ServerConfig } from '@/types'; import { apiGet, apiPost } from '../utils/fetchInterceptor'; export const useMarketData = () => { const { t } = useTranslation(); const [servers, setServers] = useState([]); const [allServers, setAllServers] = useState([]); const [categories, setCategories] = useState([]); const [tags, setTags] = useState([]); const [selectedCategory, setSelectedCategory] = useState(''); const [selectedTag, setSelectedTag] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [currentServer, setCurrentServer] = useState(null); const [installedServers, setInstalledServers] = useState([]); // Pagination states const [currentPage, setCurrentPage] = useState(1); const [serversPerPage, setServersPerPage] = useState(9); const [totalPages, setTotalPages] = useState(1); // Fetch all market servers const fetchMarketServers = useCallback(async () => { try { setLoading(true); const data: ApiResponse = await apiGet('/market/servers'); if (data && data.success && Array.isArray(data.data)) { setAllServers(data.data); // Apply pagination to the fetched data applyPagination(data.data, currentPage); } else { console.error('Invalid market servers data format:', data); setError(t('market.fetchError')); } } catch (err) { console.error('Error fetching market servers:', err); setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [t]); // Apply pagination to data const applyPagination = useCallback( (data: MarketServer[], page: number, itemsPerPage = serversPerPage) => { const totalItems = data.length; const calculatedTotalPages = Math.ceil(totalItems / itemsPerPage); setTotalPages(calculatedTotalPages); // Ensure current page is valid const validPage = Math.max(1, Math.min(page, calculatedTotalPages)); if (validPage !== page) { setCurrentPage(validPage); } const startIndex = (validPage - 1) * itemsPerPage; const paginatedServers = data.slice(startIndex, startIndex + itemsPerPage); setServers(paginatedServers); }, [serversPerPage], ); // Change page const changePage = useCallback( (page: number) => { setCurrentPage(page); applyPagination(allServers, page, serversPerPage); }, [allServers, applyPagination, serversPerPage], ); // Fetch all categories const fetchCategories = useCallback(async () => { try { const data: ApiResponse = await apiGet('/market/categories'); if (data && data.success && Array.isArray(data.data)) { setCategories(data.data); } else { console.error('Invalid categories data format:', data); } } catch (err) { console.error('Error fetching categories:', err); } }, []); // Fetch all tags const fetchTags = useCallback(async () => { try { const data: ApiResponse = await apiGet('/market/tags'); if (data && data.success && Array.isArray(data.data)) { setTags(data.data); } else { console.error('Invalid tags data format:', data); } } catch (err) { console.error('Error fetching tags:', err); } }, []); // Fetch server by name const fetchServerByName = useCallback( async (name: string) => { try { setLoading(true); const data: ApiResponse = await apiGet(`/market/servers/${name}`); if (data && data.success && data.data) { setCurrentServer(data.data); return data.data; } else { console.error('Invalid server data format:', data); setError(t('market.serverNotFound')); return null; } } catch (err) { console.error(`Error fetching server ${name}:`, err); setError(err instanceof Error ? err.message : String(err)); return null; } finally { setLoading(false); } }, [t], ); // Search servers by query const searchServers = useCallback( async (query: string) => { try { setLoading(true); setSearchQuery(query); if (!query.trim()) { // Fetch fresh data from server instead of just applying pagination fetchMarketServers(); return; } const data: ApiResponse = await apiGet( `/market/servers/search?query=${encodeURIComponent(query)}`, ); if (data && data.success && Array.isArray(data.data)) { setAllServers(data.data); setCurrentPage(1); applyPagination(data.data, 1); } else { console.error('Invalid search results format:', data); setError(t('market.searchError')); } } catch (err) { console.error('Error searching servers:', err); setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [t, allServers, applyPagination, fetchMarketServers], ); // Filter servers by category const filterByCategory = useCallback( async (category: string) => { try { setLoading(true); setSelectedCategory(category); setSelectedTag(''); // Reset tag filter when filtering by category if (!category) { fetchMarketServers(); return; } const data: ApiResponse = await apiGet( `/market/categories/${encodeURIComponent(category)}`, ); if (data && data.success && Array.isArray(data.data)) { setAllServers(data.data); setCurrentPage(1); applyPagination(data.data, 1); } else { console.error('Invalid category filter results format:', data); setError(t('market.filterError')); } } catch (err) { console.error('Error filtering servers by category:', err); setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [t, fetchMarketServers, applyPagination], ); // Filter servers by tag const filterByTag = useCallback( async (tag: string) => { try { setLoading(true); setSelectedTag(tag); setSelectedCategory(''); // Reset category filter when filtering by tag if (!tag) { fetchMarketServers(); return; } const data: ApiResponse = await apiGet( `/market/tags/${encodeURIComponent(tag)}`, ); if (data && data.success && Array.isArray(data.data)) { setAllServers(data.data); setCurrentPage(1); applyPagination(data.data, 1); } else { console.error('Invalid tag filter results format:', data); setError(t('market.tagFilterError')); } } catch (err) { console.error('Error filtering servers by tag:', err); setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [t, fetchMarketServers, applyPagination], ); // Fetch installed servers const fetchInstalledServers = useCallback(async () => { try { const data = await apiGet<{ success: boolean; data: any[] }>('/servers'); if (data && data.success && Array.isArray(data.data)) { // Extract server names const installedServerNames = data.data.map((server: any) => server.name); setInstalledServers(installedServerNames); } } catch (err) { console.error('Error fetching installed servers:', err); } }, []); // Check if a server is already installed const isServerInstalled = useCallback( (serverName: string) => { return installedServers.includes(serverName); }, [installedServers], ); // Install server to the local environment const installServer = useCallback( async (server: MarketServer, customConfig: ServerConfig) => { try { const installType = server.installations?.npm ? 'npm' : Object.keys(server.installations || {}).length > 0 ? Object.keys(server.installations)[0] : null; if (!installType || !server.installations?.[installType]) { setError(t('market.noInstallationMethod')); return false; } const installation = server.installations[installType]; // Prepare server configuration, merging with customConfig const serverConfig = { name: server.name, config: customConfig.type === 'stdio' ? { command: customConfig.command || installation.command || '', args: customConfig.args || installation.args || [], env: { ...installation.env, ...customConfig.env }, } : customConfig, }; // Call the createServer API const result = await apiPost<{ success: boolean; message?: string }>( '/servers', serverConfig, ); if (!result.success) { throw new Error(result.message || 'Failed to install server'); } // Update installed servers list after successful installation await fetchInstalledServers(); return true; } catch (err) { console.error('Error installing server:', err); setError(err instanceof Error ? err.message : String(err)); return false; } }, [t, fetchInstalledServers], ); // Change servers per page const changeServersPerPage = useCallback( (perPage: number) => { setServersPerPage(perPage); setCurrentPage(1); applyPagination(allServers, 1, perPage); }, [allServers, applyPagination], ); // Load initial data useEffect(() => { fetchMarketServers(); fetchCategories(); fetchTags(); fetchInstalledServers(); }, [fetchMarketServers, fetchCategories, fetchTags, fetchInstalledServers]); return { servers, allServers, categories, tags, selectedCategory, selectedTag, searchQuery, loading, error, setError, currentServer, fetchMarketServers, fetchServerByName, searchServers, filterByCategory, filterByTag, installServer, // Pagination properties and methods currentPage, totalPages, serversPerPage, changePage, changeServersPerPage, // Installed servers methods isServerInstalled, }; };